From 53b947df484159f5934898ba6c0aa69a9c869007 Mon Sep 17 00:00:00 2001 From: Ido Hadanny Date: Thu, 22 Sep 2011 21:30:13 +0300 Subject: added stuff --- .../com/hackingroomba/roombacomm/ArduinoBot.java | 379 ++++ .../hackingroomba/roombacomm/ArduinoClient.java | 110 + .../roombacomm/AudioLocalizerClient.java | 258 +++ .../src/com/hackingroomba/roombacomm/BumpTurn.java | 129 ++ .../src/com/hackingroomba/roombacomm/Drive.java | 141 ++ .../hackingroomba/roombacomm/DriveRealTime.java | 173 ++ .../hackingroomba/roombacomm/FrameProcessor.java | 586 +++++ .../com/hackingroomba/roombacomm/JImagePanel.java | 20 + .../hackingroomba/roombacomm/ListSerialPorts.java | 59 + .../src/com/hackingroomba/roombacomm/LogoA.java | 105 + .../src/com/hackingroomba/roombacomm/Note.java | 21 + .../src/com/hackingroomba/roombacomm/Pid.java | 67 + .../com/hackingroomba/roombacomm/RTTTLParser.java | 112 + .../com/hackingroomba/roombacomm/RTTTLPlay.java | 144 ++ .../com/hackingroomba/roombacomm/RoboRealmAPI.java | 945 +++++++++ .../hackingroomba/roombacomm/RoboRealmTest.java | 240 +++ .../src/com/hackingroomba/roombacomm/Roborama.java | 910 ++++++++ .../hackingroomba/roombacomm/RobotConnection.java | 364 ++++ .../com/hackingroomba/roombacomm/RobotType.java | 75 + .../com/hackingroomba/roombacomm/RoombaComm.java | 1690 +++++++++++++++ .../hackingroomba/roombacomm/RoombaCommCLI.java | 1571 ++++++++++++++ .../hackingroomba/roombacomm/RoombaCommFrame.java | 2239 ++++++++++++++++++++ .../hackingroomba/roombacomm/RoombaCommGUI.java | 125 ++ .../hackingroomba/roombacomm/RoombaCommPanel.java | 1854 ++++++++++++++++ .../hackingroomba/roombacomm/RoombaCommSerial.java | 505 +++++ .../roombacomm/RoombaCommTCPClient.java | 451 ++++ .../hackingroomba/roombacomm/RoombaCommTest.java | 102 + .../roombacomm/RoombaCommTestOld.java | 387 ++++ .../hackingroomba/roombacomm/RoombaRecorder.java | 591 ++++++ .../roombacomm/RoombaRecorderPanel.java | 582 +++++ .../com/hackingroomba/roombacomm/SimpleTest.java | 148 ++ .../src/com/hackingroomba/roombacomm/Spiral.java | 127 ++ .../src/com/hackingroomba/roombacomm/Spiro1.java | 139 ++ .../src/com/hackingroomba/roombacomm/Spiro2.java | 144 ++ .../src/com/hackingroomba/roombacomm/Spy.java | 132 ++ .../src/com/hackingroomba/roombacomm/SpyAuto.java | 147 ++ .../com/hackingroomba/roombacomm/SpySimple.java | 136 ++ .../src/com/hackingroomba/roombacomm/Test.java | 177 ++ .../src/com/hackingroomba/roombacomm/Tribble.java | 111 + .../src/com/hackingroomba/roombacomm/Waggle.java | 115 + .../src/com/hackingroomba/roombacomm/XML.java | 173 ++ .../roombacomm/images/but_backward.png | Bin 0 -> 1058 bytes .../roombacomm/images/but_checkrobotOn.png | Bin 0 -> 518 bytes .../roombacomm/images/but_cleanOn.png | Bin 0 -> 400 bytes .../hackingroomba/roombacomm/images/but_dirtOn.png | Bin 0 -> 354 bytes .../hackingroomba/roombacomm/images/but_dockOn.png | Bin 0 -> 448 bytes .../roombacomm/images/but_forward.png | Bin 0 -> 1061 bytes .../hackingroomba/roombacomm/images/but_maxOn.png | Bin 0 -> 345 bytes .../roombacomm/images/but_spinleft.png | Bin 0 -> 1339 bytes .../roombacomm/images/but_spinright.png | Bin 0 -> 1367 bytes .../hackingroomba/roombacomm/images/but_spotOn.png | Bin 0 -> 423 bytes .../hackingroomba/roombacomm/images/but_stop.png | Bin 0 -> 990 bytes .../roombacomm/images/but_transport_play.png | Bin 0 -> 2795 bytes .../roombacomm/images/but_transport_record.png | Bin 0 -> 2860 bytes .../roombacomm/images/but_transport_stop.png | Bin 0 -> 2549 bytes .../roombacomm/images/but_turnleft.png | Bin 0 -> 1182 bytes .../roombacomm/images/but_turnright.png | Bin 0 -> 1184 bytes .../hackingroomba/roombacomm/net/DumpMethods.java | 70 + .../roombacomm/net/RoombaCommTCPServer.java | 94 + .../hackingroomba/roombacomm/net/SimpleTest.java | 131 ++ .../roombacomm/net/TextHttpServer.java | 109 + .../com/hackingroomba/roombacomm/watchVideo.java | 255 +++ 62 files changed, 17143 insertions(+) create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoBot.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoClient.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/AudioLocalizerClient.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/BumpTurn.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/Drive.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/DriveRealTime.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/FrameProcessor.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/JImagePanel.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/ListSerialPorts.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/LogoA.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/Note.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/Pid.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLParser.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLPlay.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmAPI.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmTest.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/Roborama.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RobotConnection.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RobotType.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommCLI.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommFrame.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommGUI.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommPanel.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommSerial.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTCPClient.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTest.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTestOld.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorder.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorderPanel.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/SimpleTest.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/Spiral.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/Spiro1.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/Spiro2.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/Spy.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/SpyAuto.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/SpySimple.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/Test.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/Tribble.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/Waggle.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/XML.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_backward.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_checkrobotOn.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_cleanOn.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dirtOn.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dockOn.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_forward.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_maxOn.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinleft.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinright.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spotOn.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_stop.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_play.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_record.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_stop.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnleft.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnright.png create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/net/DumpMethods.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/net/RoombaCommTCPServer.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/net/SimpleTest.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/net/TextHttpServer.java create mode 100644 roombacomm-client/src/com/hackingroomba/roombacomm/watchVideo.java (limited to 'roombacomm-client/src/com') diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoBot.java b/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoBot.java new file mode 100644 index 0000000..d029413 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoBot.java @@ -0,0 +1,379 @@ +/* + * roombacomm.ArduinoClient -- test out the Arduino subsystem without robot motion + * + * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net + * + * 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 com.hackingroomba.roombacomm; + +import com.hackingroomba.roombacomm.RobotConnection.ReadTerminator; +import com.hackingroomba.roombacomm.RobotType.robotTypes; + +import java.io.*; + +/** + * The extensions to roombacomm for ArduinoBot operations. + * + *

Overview

+ * This class contains the operations available from an ArduinoBot +**/ + +public class ArduinoBot extends RoombaComm { + + // connection variables + private Integer compassHeading; + private String arduinoString; + private RobotConnection robotConnection; + private RobotType robotType; + + // compass variables + private int compassOffset = 0; + private boolean compassOffsetInitialized = false; + + // encoder variables + private int encoder; + + private long startTime; + public long elapsedTime; + + public ArduinoBot() { + super(); + } + + public ArduinoBot(RobotConnection robotConnection) { + super(robotConnection); + this.robotConnection = robotConnection; + } + + /** + * Initialize an Arduino robot controller + */ + public boolean initArduinoBot(RobotType rt) + { + byte[] robotTypeMsg = "robot m\r".getBytes(); // message to Arduino to tell it the robot type + + robotType = rt; // record robotType for later use + System.out.println("checking for ArduinoBot... \n"); + try { + Thread.sleep(2000); + } catch (Exception e) {System.out.println("Exception in Thread.sleep()"); } + + // send the robot type message to Arduino, so it knows how to read the encoders, and any other robot-specific things + if (robotType.robotType == RobotType.robotTypes.frankenRoomba) { + System.out.println("Sending robot r to Arduino"); + robotTypeMsg = "robot r\r".getBytes(); + } else if (robotType.robotType == robotTypes.tankbot) { + System.out.println("Sending robot t to Arduino"); + robotTypeMsg = "robot t\r".getBytes(); + } + send(robotTypeMsg); + + printCompass(); + printEncoders(); + return true; + } + + + /** + * Send a single byte to the ArduinoBot + * (defined as int because of stupid java signed bytes). SendToArduino method of RobotConnection + * is used because this is an ardunobot, which requires a leading m and trailing to make + * the parser work right + * @param b byte of an Arduino command to send + * @return true on successful send + */ + public boolean send(int b) { + return(robotConnection.sendToArduino(b)); + } + + /** + * Send a byte array to the ArduinoBot. If the first byte is 128 or higher, preface the byte string with m + * so it's treated by Arduino as a roomba command + */ + public boolean send(byte[] bytes) { + if (bytes[0] < 0) // must be a roomba command + return(robotConnection.sendToArduino(bytes)); + else + return(robotConnection.send(bytes)); + } + + /** + * Read a string from Arduino up to a + * @return array of bytes read from ArduinoBot + */ + public String readArduinoBot() throws Exception { + return(robotConnection.readBotToTerminator(ReadTerminator.LF)); + } + + public void printCompass() { + int compassReading; + startTime = System.currentTimeMillis(); + compassReading = getCompass(); + if (compassReading < 0) return; + elapsedTime = System.currentTimeMillis() - startTime; + System.out.println("Heading: " + compassReading + " in " + elapsedTime + " ms"); + } + + public int getCompass() { + // read the uncorrected heading + try { + compassHeading = getUncorrectedCompass(); + } catch (Exception e) { + System.out.println("Exception reading uncorrected compass\n" + e.getMessage() + "\n" ); + e.printStackTrace(); + return -1; + } + // if we haven't initialized the compass offset yet, try to read an offset from a config file + // compassOffsetInitialized ensures we only try this once + try { + if (!compassOffsetInitialized) { // get the compass offset from file if it exists + compassOffset = readConfigInt(new String("compass_offset")); + compassOffsetInitialized = true; + } + } catch (Exception e) { + System.out.println("No compass_offset file could be opened\n" + e.getMessage()); + compassOffsetInitialized = true; + } + + // offset the uncorrected heading by the compassOffset, which depends on mounting position + compassHeading -= compassOffset; + compassHeading %= 360; // compensate for offset which can make it exceed 360 + if (compassHeading < 0) compassHeading += 360; + return compassHeading; + } + + private int getUncorrectedCompass() throws Exception { + byte [] compassCmd = {'c', '\r'}; + int heading; + + //System.out.println("getUncorrectedCompass"); + startTime = System.currentTimeMillis(); + if (!send(compassCmd)) { + System.out.println("Error sending c command"); + } + try { + arduinoString = robotConnection.readBotToTerminator(ReadTerminator.LF); + if (arduinoString == null) { + System.out.println("Error: getUncorrectedCompass failed to read heading - null string"); + } + } catch (Exception e) { + elapsedTime = System.currentTimeMillis() - startTime; + System.err.println("Exception reading compass in readBotToTerminator in " + elapsedTime + " ms"); + e.printStackTrace(); + throw e; + } + elapsedTime = System.currentTimeMillis() - startTime; + + //System.out.println("received string: " + arduinoString); + String headingString = arduinoString.split("\\s")[0]; + heading = new Integer(headingString); + return heading; + } + + public boolean setCompassOffset(int currentMagHeading) + { + int heading; + + if ((currentMagHeading < 0) || (currentMagHeading > 360)) { + System.out.println("currentMagHeading must be between 0 and 360"); + return false; + } + try { + heading = getUncorrectedCompass(); + if (heading < 0) return false; + compassOffset = heading - currentMagHeading; + if (compassOffset < 0) compassOffset += 360; + writeConfigInt(new String("compass_offset"), compassOffset); + } catch (Exception e) { + System.err.println("Error getting compass or writing compass offset"); + e.printStackTrace(); + return false; + } + compassOffsetInitialized = true; + return true; + } + + /* + * Print, Get the current wheel encoders reading + */ + public void printEncoders() + { + double readEncDistance = 0; + + startTime = System.currentTimeMillis(); + try { + readEncDistance = getEncoders(); + } catch (Exception e) { + System.err.println("Exception reading robot encoders: " + e.getMessage()); + } + long elapsedTime = System.currentTimeMillis() - startTime; + System.out.println("Wheel encoders reading: " + readEncDistance + " in " + elapsedTime + " ms"); + + } + + public double getEncoders() throws Exception { + byte [] cmd = {'e', '\r'}; + + //System.out.println("read encoders"); + startTime = System.currentTimeMillis(); + if (!send(cmd)) { + System.out.println("Error sending e command"); + } + try { + arduinoString = robotConnection.readBotToTerminator(ReadTerminator.LF); + elapsedTime = System.currentTimeMillis() - startTime; + if (arduinoString == null) { + System.err.println("Error: getEncoders failed to read encoders, null returned in " + elapsedTime + " ms"); + } + } catch (Exception e) { + elapsedTime = System.currentTimeMillis() - startTime; + System.err.println("Exception reading encoders from readBotToTerminator in " + elapsedTime + " ms"); + e.printStackTrace(); + throw e; + } + + //System.out.println("received string: " + arduinoString); + String [] encoderStrings = arduinoString.split("\\s"); + if (encoderStrings.length == 1) { + encoder = new Integer(encoderStrings[0]); + } else { + System.out.println("Error reading encoders - expected 1 string, found:" + encoderStrings.length + + " in: " + arduinoString + " in " + elapsedTime + " ms"); + return -1; + } + + double encoderDistance = encoder /robotType.countsPerInch; + return encoderDistance; + } + + + /** + * Read roomba 26-byte sensor record using robotConnection. Tries once to read valid data, allowing 100ms + * timeout on each attempt. + * @return true if read 26 bytes of valid data. Data has been stored in sensor_bytes. False otherwise + */ + public boolean updateSensors() + { + return updateSensors(SENSORS_ALL); + } + + public boolean updateSensors(int sensorGroup) + { + int sensorGroupSize; + + if (robotConnection == null) { + System.out.println("Error at ArduinoBot.updateSensors(): no connection object for robot"); + return false; + } + + switch(sensorGroup) { + case SENSORS_ALL: sensorGroupSize = 26; break; + case 100: sensorGroupSize = 80; break; + default: + System.err.println("Invalid sensor group in updateSensors(): " + sensorGroup); + return false; + } + sensors(sensorGroup); + return getSensorData(sensorGroupSize); + } + + /** + * Writes an int to the named file + * @throws FileNotFoundException If it can't open the file + * @throws IOException If it can't write to the file + */ + public void writeConfigInt(String filename, int value) throws FileNotFoundException, IOException { + DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File(filename))); + dos.writeInt(value); + } + + /** + * Read a configuration file containing an int. + * @param filename The file to open & read an int from + * @return Int read from file + * @throws Exception related to attempting to open & read int from file + */ + public int readConfigInt(String filename) throws Exception { + int configInt = -1; + + try { + DataInputStream dis = new DataInputStream(new FileInputStream(new File(filename))); + configInt = dis.readInt(); + + } catch (Exception e) { + System.out.println("No config file could be read\n" + e.getMessage()); + } + return configInt; + } + + + /** + * Writes a double to the named file + * @throws FileNotFoundException If it can't open the file + * @throws IOException If it can't write to the file + */ + public void writeConfigDouble(String filename, double value) throws FileNotFoundException, IOException { + DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File(filename))); + dos.writeDouble(value); + } + + /** + * Read a configuration file containing a double. + * @param filename The file to open & read a double from + * @return double read from file + * @throws Exception related to attempting to open & read double from file + */ + public double readConfigDouble(String filename) throws Exception { + double configDouble = -1; + + try { + DataInputStream dis = new DataInputStream(new FileInputStream(new File(filename))); + configDouble = dis.readDouble(); + + } catch (Exception e) { + System.out.println("No config file could be read\n" + e.getMessage()); + throw(e); + } + return configDouble; + } + + public int getEncoder() { + return encoder; + } + + /** + * Bogus methods which should never be called on ArduinoClient + */ + public String [] listPorts() + { + System.out.println("Error at ArduinoBot.listPorts(): should never get here"); + return (new String[] {"Error: listports calledon ArdunoBot; not supported"}); + } + public boolean connect(String s) + { + System.out.println("Error at ArduinoBot.connect(): should never get here"); + return false; + } + public void disconnect() + { + System.out.println("Error at ArduinoBot.disconnect(): should never get here"); + return; + } + +} \ No newline at end of file diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoClient.java b/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoClient.java new file mode 100644 index 0000000..6ea2843 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/ArduinoClient.java @@ -0,0 +1,110 @@ +/* + * roombacomm.ArduinoClient -- test out the Arduino subsystem without robot motion + * + * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net + * + * 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 com.hackingroomba.roombacomm; + +import jargs.gnu.CmdLineParser; +import java.io.*; +import java.net.Socket; +import java.net.UnknownHostException; + + +/** + * A test class for Arduino communications. + * + *

Overview

+ * This class contains the communications layer-independent parts of + * how to communicate with an Arduino. It does assume a very serial port-like + * interaction. +**/ + +public class ArduinoClient { + private static final long serialVersionUID = 1L; + private static final int lumBufHeight = 32; + + String usage = + "Usage: \n"+ + " ArduinoClient {--arduinoServer|-s} [{--arduinoPortNum|-p} ] [options]\n" + + "where [options] can be one or more of:\n"+ + " --debug|-X -- turn on debug output\n"; + + private RobotConnection arduinoConnection; + private String arduinoServer; + private Boolean debug; + + public ArduinoClient() { + // constructor, mustn't throw exceptions. Do nothing for now + } + + // main - it all starts here; when run as main class, ask Arduino for its compass reading + public static void main(String[] args) { + ArduinoClient l = new ArduinoClient(); + l.testClient(args); + } + + public void testClient(String[] args) { + parseCmd(args); + arduinoConnection = new RobotConnection(arduinoServer); + arduinoConnection.connect(); + ArduinoBot arduinobot = new ArduinoBot(arduinoConnection); + arduinobot.printCompass(); + arduinoConnection.disconnect(); + } + + + public void parseCmd(String[] args){ + //System.out.println("*** running ArduinoClient standalone WparseCmd"); + + CmdLineParser parser = new CmdLineParser(); + CmdLineParser.Option debugOption = parser.addBooleanOption('X', "debug"); + CmdLineParser.Option arduinoServerOption = parser.addStringOption('s', "arduinoServer"); + + try { + parser.parse(args); + } + catch ( CmdLineParser.OptionException e ) { + System.err.println(e.getMessage()); + System.out.println("parseCmd had an error\n"+ usage ); + System.exit(2); + } + + // String portname = args[0]; // e.g. "/dev/cu.KeySerial1", or "COM5" or "192.168.1.1:5002" + arduinoServer = (((String)parser.getOptionValue(arduinoServerOption))); + Boolean debugBool = (Boolean)parser.getOptionValue(debugOption,new Boolean(false)); + setDebug(debugBool.booleanValue()); + //System.out.println("debug is ("+isDebug()+")"); + //System.out.println("*** end of parseCmd"); + } + + + public boolean isDebug() { + return debug; + } + public void setDebug(boolean debug_) { + debug = debug_; + } + /** + * @param arduuinoServer the localizerServer to set + */ + + } + + \ No newline at end of file diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/AudioLocalizerClient.java b/roombacomm-client/src/com/hackingroomba/roombacomm/AudioLocalizerClient.java new file mode 100644 index 0000000..cb99e6a --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/AudioLocalizerClient.java @@ -0,0 +1,258 @@ +/* + * roombacomm.AudioLocalizerClient -- test out the audio localizer subsystem without robot motion + * + * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net + * + * 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 com.hackingroomba.roombacomm; + +import jargs.gnu.CmdLineParser; +import java.io.*; +import java.net.Socket; +import java.net.UnknownHostException; + + +/** + Run it with something like:
+	    java roombacomm.AudioLocalizerClient --localizerServer 192.168.0.150 --localizerPortNum 5005 --debug
+ Usage: + roombacomm.AudioLocalizerClient --localizerServer --localizerPortNum [options]
+ where + [options] can be one or more of: + -debug -- turn on debug output +
+*/ + public class AudioLocalizerClient { + private static final long serialVersionUID = 1L; + private static final int lumBufHeight = 32; + + String usage = + "Usage: \n"+ + " AudioLocalizerClient --localizerServer --localizerPortNum [options]\n" + + "where [options] can be one or more of:\n"+ + " --debug -- turn on debug output\n"; + boolean debug = false; + private int waittime; + private String localizerServer = ""; + private int localizerPortNum = 5010 ; + private int locationStringSize = 200; + private byte[] locationBytes = new byte[locationStringSize]; + private byte[] locationStringBuf = new byte[locationStringSize]; + private double x, y; + + // socket variables + Socket localizerSocket; + BufferedInputStream in; + BufferedOutputStream out; + + public AudioLocalizerClient() { + // constructor, mustn't throw exceptions. Do nothing for now + } + + // main - it all starts here + public static void main(String[] args) { + AudioLocalizerClient l = new AudioLocalizerClient(); + l.parseCmd(args); + l.connectLocalizer(); + l.printLocation(); + } + + public void connectLocalizer() + { + if (getLocalizerServer() == null || getLocalizerServer().length() == 0) { + System.err.println(" you must supply a --localizerServer value to use the command"); + if (getLocalizerPortNum() <= 0) { + System.err.println(" you must supply a --localizerPortNum value to use the command"); + } + System.exit(6); + } + if (getLocalizerPortNum() <= 0) { + System.err.println(" you must supply a --localizerPortNum value to use the command \"getVideo\""); + System.exit(7); + } + + connect(); + } + + public void printLocation() + { + //while (true) { + int size = readLocation(); + String locationString = new String(locationBytes, 0, size); + System.out.println("received string: " + locationString); + String [] splitLocationStrings = locationString.split("\\s"); + if (splitLocationStrings[0].compareTo("Invalid") == 0) { + System.out.println("Location is invalid"); + } else { + x = new Double(splitLocationStrings[1]); + y = new Double(splitLocationStrings[2]); + System.out.println("X: " + x + " Y: " + y); + } + //} + } + public int readLocation() + { + int readLength = 0; + int maxReadSize = locationStringSize; + locationBytes[0] = 'L'; // send the "localize" command + int locStringIx = 0; + + try { + out.write(locationBytes, 0, 1); + out.flush(); + + // ACHTUNG - locationStringBuf gets overwritten at the beginning by multiple read buffers - use locationString + for (int i=0; i<5; i++) { + readLength = in.read(locationStringBuf, 0, maxReadSize); + maxReadSize -= readLength; + for (int j=0; j + Run it with something like:
+    java roombacomm.BumpTurn /dev/cu.KeySerial1
+ Usage: + roombacomm.Drive serialportname [protocol] [options] + where: + protocol (optional) is SCI or OI + [options] can be one or more of: + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -nohwhandshake -- don't use hardware-handshaking +
+ +*/ +public class BumpTurn { + + static String usage = + "Usage: \n"+ + " roombacomm.Drive [protocol]" + + "" + + " [options]\n" + + "where protocol (optional) is SCI or OI and [options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + "nohwhandshake -- don't use hardware-handshaking" + + "\n"; + static boolean debug = false; + static boolean hwhandshake = false; + + public static void main(String[] args) { + if( args.length < 1 ) { + System.out.println( usage ); + System.exit(0); + } + + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(); + for( int i=1; i < args.length; i++ ) { + if (args[i].equals("SCI") || (args[i].equals("OI"))) { + roombacomm.setProtocol(args[i]); + } else if( args[i].endsWith("debug") ) + debug = true; + else if( args[i].endsWith("nohwhandshake") ) + roombacomm.setWaitForDSR(false); + else if( args[i].endsWith("hwhandshake") ) + roombacomm.setWaitForDSR(true); + } + + roombacomm.debug = debug; + + if( ! roombacomm.connect( portname ) ) { + System.out.println("Couldn't connect to "+portname); + System.exit(1); + } + + System.out.println("Roomba startup"); + roombacomm.startup(); + roombacomm.control(); + roombacomm.pause(100); + + roombacomm.updateSensors(); + + System.out.println("Press return to exit."); + boolean done = false; + while( !done ) { + if( roombacomm.bumpLeft() ) { + roombacomm.spinRight(90); + } + else if( roombacomm.bumpRight() ) { + roombacomm.spinLeft(90); + } + else if( roombacomm.wall() ) { + roombacomm.playNote( 72,10 ); // beep! + } + roombacomm.goForward(); + roombacomm.updateSensors(); + + done = keyIsPressed(); + } + + roombacomm.stop(); + + System.out.println("Disconnecting"); + roombacomm.disconnect(); + + System.out.println("Done"); + } + + /** check for keypress, return true if so */ + public static boolean keyIsPressed() { + boolean press = false; + try { + if( System.in.available() != 0 ) { + System.out.println("key pressed"); + press = true; + } + } catch( IOException ioe ) { } + return press; + } +} + diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Drive.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Drive.java new file mode 100644 index 0000000..57685bc --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Drive.java @@ -0,0 +1,141 @@ +/* + * roombacomm.Drive -- test out the DRIVE command + * + * 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 com.hackingroomba.roombacomm; + +import java.io.*; + +/** + * A program for driving Roomba. + *

+ * Run it with something like:

+ *   java roombacomm.Drive /dev/cu.KeySerial1 byte1 byte2 byte3 byte4
+ * Usage: + * roombacomm.Drive serialportname [protocol] velocity radius waittime [options]
+ * where protocol (optional) is SCI or OI + * velocity is in mm/sec + * radius is mm from the centerpoint + * waittime is in milliseconds + * [options] can be one or more of: + * -debug -- turn on debug output + * -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + * -nohwhandshake -- don't use hardware-handshaking + *
+ * + */ +public class Drive { + + static String usage = + "Usage: \n"+ + " roombacomm.Drive [protocol] [options]\n" + + "where protocol (optional) is SCI or OI\n" + + "[options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + " -nohwhandshake -- don't use hardware-handshaking\n"+ + "\n"; + static boolean debug = false; + static boolean hwhandshake = false; + + public static void main(String[] args) { + new Drive(args); + } + + Drive(String[] args) + { + int argIndx; + String portname, protocol; + if( args.length < 4 ) { + System.out.println( usage ); + System.exit(0); + } + + /* + * Parse port & protocol + */ + portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(); + int argOffset = 0; + if (args[1].equals("SCI") || (args[1].equals("OI"))) { + roombacomm.setProtocol(args[1]); + argOffset = 1; + } + + /* + * Parse command arguments + */ + int velocity=0, radius=0, waittime=0; + try { + velocity = Integer.parseInt( args[1+argOffset] ); // velocity would be the 1st numeric + radius = Integer.parseInt( args[2+argOffset] ); + waittime = Integer.parseInt( args[3+argOffset] ); + } catch( Exception e ) { + System.err.println("Couldn't parse velocity, radius, or waittime"); + System.exit(1); + } + + + /* + * Parse options + */ + for( int i=4+argOffset; i < args.length; i++ ) { + if( args[i].endsWith("debug") ) + debug = true; + else if( args[i].endsWith("nohwhandshake") ) + roombacomm.setWaitForDSR(false); + else if( args[i].endsWith("hwhandshake") ) + roombacomm.setWaitForDSR(true); + } + + roombacomm.debug = debug; + + if( ! roombacomm.connect( roombacomm.getPortname())) { + System.out.println("Couldn't connect to "+portname); + System.exit(1); + } + System.out.println("Using port: " + roombacomm.getPortname() + " protocol: " + + roombacomm.getProtocol() + " velocity: " + velocity + " radius: " + + radius + " waittime: " + waittime + "\n"); + + System.out.println("Roomba startup"); + roombacomm.startup(); + roombacomm.pause(100); + roombacomm.control(); + roombacomm.playNote( 72, 10 ); // C , test note + roombacomm.pause( 200 ); + if( roombacomm.updateSensors() ) + System.out.println("Roomba found!\n"); + else + System.out.println("No Roomba. :( Is it turned on?\n"); + + roombacomm.drive( velocity, radius ); + roombacomm.pause(waittime); + roombacomm.stop(); + + System.out.println("Disconnecting"); + roombacomm.disconnect(); + + System.out.println("Done"); + } + +} + diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/DriveRealTime.java b/roombacomm-client/src/com/hackingroomba/roombacomm/DriveRealTime.java new file mode 100644 index 0000000..b52fcf8 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/DriveRealTime.java @@ -0,0 +1,173 @@ +/* + * roombacomm.Drive -- test out the DRIVE command + * + * 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 com.hackingroomba.roombacomm; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; + + +/** + Drive the Roomba with the arrow keys in real-time +

+ Run it with something like:

+    java roombacomm.DriveRealTime /dev/cu.KeySerial1
+ Usage: + roombacomm.DriveRealTime serialportname [protocol] [options]
+ where: + protocol (optional) is SCI or OI + [options] can be one or more of: + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -nohwhandshake -- don't use hardware-handshaking +
+*/ +public class DriveRealTime extends JFrame implements KeyListener { + + static String usage = + "Usage: \n"+ + " roombacomm.DriveRealTime [protocol] [options] \n" + + "where: protocol (optional) is SCI or OI\n" + + "[options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + "-nohwhandshake -- don't use hardware-handshaking\n"+ + "\n"; + static boolean debug = false; + static boolean hwhandshake = false; + + RoombaCommSerial roombacomm; + JTextArea displayText; + + public static void main(String[] args) { + if( args.length < 1 ) { + System.out.println( usage ); + System.exit(0); + } + new DriveRealTime(args); + } + + public DriveRealTime(String[] args) { + super("DriveRealTime"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + String portname = args[0]; + roombacomm = new RoombaCommSerial(); + for( int i=1; i < args.length; i++ ) { + if (args[i].equals("SCI") || (args[i].equals("OI"))) { + roombacomm.setProtocol(args[i]); + } else if( args[i].endsWith("debug") ) + debug = true; + else if( args[i].endsWith("nohwhandshake") ) + roombacomm.setWaitForDSR(false); + else if( args[i].endsWith("hwhandshake") ) + roombacomm.setWaitForDSR(true); + } + + roombacomm.debug = debug; + + if( ! roombacomm.connect( portname ) ) { + System.out.println("Couldn't connect to "+portname); + System.exit(1); + } + + System.out.println("Roomba startup"); + roombacomm.startup(); + roombacomm.control(); + roombacomm.pause(50); + + setupWindow(); + + updateDisplay("click on this window\nthen use arrow keys to drive Roomba around.\n"); + } + + /** Handle the key pressed event from the text field. */ + public void keyPressed(KeyEvent e) { + int keyCode = e.getKeyCode(); + if( keyCode == KeyEvent.VK_SPACE ) { + updateDisplay("stop"); + roombacomm.stop(); + } + else if( keyCode == KeyEvent.VK_UP ) { + updateDisplay("forward"); + roombacomm.goForward(); + } + else if( keyCode == KeyEvent.VK_DOWN ) { + updateDisplay("backward"); + roombacomm.goBackward(); + } + else if( keyCode == KeyEvent.VK_LEFT ) { + updateDisplay("spinleft"); + roombacomm.spinLeft(); + } + else if( keyCode == KeyEvent.VK_RIGHT ) { + updateDisplay("spinright"); + roombacomm.spinRight(); + } + else if( keyCode == KeyEvent.VK_COMMA ) { + updateDisplay("speed down"); + roombacomm.setSpeed( roombacomm.getSpeed() - 50 ); + } + else if( keyCode == KeyEvent.VK_PERIOD ) { + updateDisplay("speed up"); + roombacomm.setSpeed( roombacomm.getSpeed() + 50 ); + } + else if( keyCode == KeyEvent.VK_R ) { + updateDisplay("reset"); + roombacomm.reset(); + roombacomm.control(); + } + } + + /** Handle the key released event from the text field. */ + public void keyReleased(KeyEvent e) { + } + + /** Handle the key typed event from the text field. */ + public void keyTyped(KeyEvent e) { + } + + /** a sort of gui equivalent to system.out.println */ + public void updateDisplay( String s ) { + displayText.append( s+"\n" ); + displayText.setCaretPosition(displayText.getDocument().getLength()); + } + + /** + */ + public void setupWindow() { + displayText = new JTextArea(20,30); + displayText.setLineWrap(true); + displayText.setEditable(false); + displayText.addKeyListener(this); + JScrollPane scrollPane = new JScrollPane(displayText, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER ); + Container content = getContentPane(); + content.add( scrollPane, BorderLayout.CENTER ); + addKeyListener(this); + pack(); + setResizable(false); + setVisible(true); + } +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/FrameProcessor.java b/roombacomm-client/src/com/hackingroomba/roombacomm/FrameProcessor.java new file mode 100644 index 0000000..85d7b60 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/FrameProcessor.java @@ -0,0 +1,586 @@ +package com.hackingroomba.roombacomm; +import java.io.*; +import java.net.*; +import java.awt.*; +import java.awt.image.*; +import java.awt.event.*; +import javax.swing.*; +import java.lang.Thread; +import java.util.*; +import java.lang.Math; + + +public class FrameProcessor { + private static final long serialVersionUID = 1L; + private static final int lumBufHeight = 32; + // socket variables + int portNum = 5005; + Socket videoSocket; + BufferedInputStream in; + BufferedOutputStream out; + RoboRealmAPI rr; + + // video variables + byte[] readBuf; // where we read raw network data into + byte[] vidBuf; // raw pixel bytes + int[] vidDispBuf, sliceBufInt; + int[] lumBuf = new int[256*lumBufHeight]; + int[] qDisplayBuf, q; // the quantized array, qDisplayBuf is video data, q is 0 or 1 in each int + int[] ltBuf; // the graphic markers for current middle & bundaries of tracking + String server = ""; + int frameSize; + int imgWidth; + int imgHeight; + Container contentPane; + Image img, lumImg, quantizedImg, sliceImg, trackingImg; + JFrame frame; + Insets insets; + int qStartRow, qEndRow; + int thresholdOverride = 0; + int frameCount = 0; + + // line variables. Units are pixels distance from center of image. Negative value is left of center + class LineBoundary { + int start; + int middle; + int end; + }; + ArrayList lineList; + LineBoundary currentLine; + int first; + int[] smoothSlice; // a quantized slice across the image, averaged to remove noise + int minLineThickness, maxLineThickness; + + public FrameProcessor(String server,int portnum, int width, int height, int minLine, int maxline, int thresholdOv) + { + imgWidth = width; + imgHeight = height; + minLineThickness = minLine; + maxLineThickness = maxline; + lineList = new ArrayList(); + currentLine = new LineBoundary(); + first = 1; + smoothSlice = new int[width]; + ltBuf = new int[4*imgWidth]; + thresholdOverride = thresholdOv; + this.server = server; + this.portNum = portnum; + } + + public void makeImagePane() + { + javax.swing.SwingUtilities.invokeLater(new Runnable() { + public void run() { + createAndShowGUI(); + } + }); + } + + void createAndShowGUI() { + //Create and set up the window. + frame = new JFrame("Raw Image"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + contentPane = frame.getContentPane(); + contentPane.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 20)); + + //Display the window. + frame.pack(); + frame.setVisible(true); + insets = frame.getInsets(); + frame.setMinimumSize(new Dimension(400,350)); + + } + + public void displayFrame() + { + Graphics g = frame.getGraphics(); + + // display the raw image + img = frame.createImage(new MemoryImageSource(imgWidth,imgHeight,vidDispBuf, 0, imgWidth)); + g.drawImage(img, insets.left+2, insets.top+2, null); + + // display the slice + sliceImg = frame.createImage(new MemoryImageSource(imgWidth, qEndRow-qStartRow, sliceBufInt, 0, imgWidth)); + g.drawImage(sliceImg, insets.left, insets.top + 20 + imgHeight, null); + + // display the quantized image + quantizedImg = frame.createImage(new MemoryImageSource(imgWidth, qEndRow-qStartRow, qDisplayBuf, 0, imgWidth)); + g.drawImage(quantizedImg, insets.left, insets.top + 40 + imgHeight, null); + + // display the line tracking markers + trackingImg = frame.createImage(new MemoryImageSource(imgWidth, 4, ltBuf, 0, imgWidth)); + g.drawImage(trackingImg, insets.left, insets.top + 60 + imgHeight, null); + + //lumImg = frame.createImage(new MemoryImageSource(256,lumBufHeight,lumBuf, 0, 256)); + //g.drawImage(lumImg, insets.left, insets.top + 20 + imgHeight, null); + } + + public Boolean frame2Roborealm() + { + return rr.setImage(vidBuf, imgWidth, imgHeight); + } + + public String getShapeData() + { + String rrString = rr.getVariable("SHAPES"); + //System.out.println(rrString); + return rrString; + } + + public void connect(boolean connectRoborealm) + { + try { + videoSocket = new Socket(server, portNum); + in = new BufferedInputStream(videoSocket.getInputStream()); + out = new BufferedOutputStream(videoSocket.getOutputStream()); + } catch (UnknownHostException e) { + System.out.println("Unknown host: " + server + ":" + portNum); + System.exit(-1); + } catch(IOException e) { + System.out.println("I/O exception"); + e.printStackTrace(); + System.exit(-1); + } + System.out.println("connected to video"); + + // connect to RoboRealm if requested + if (connectRoborealm) { + rr = new RoboRealmAPI(); + if (!rr.connect("localhost")) + { + System.out.println("Could not connect to RoboRealm on localhost! Exiting..."); + System.exit(-1); + } + } + } + + public void disconnect() + { + try { + // do io streams need to be closed first? + if (in != null) in.close(); + } catch (Exception e) { + e.printStackTrace(); + } + in = null; + + try { + if (videoSocket != null) videoSocket.close(); + } catch (Exception e) { + e.printStackTrace(); + } + videoSocket = null; + System.out.println("disconnected from Video"); + } + + /** + * Read a video frame from the network device + * @param width in pixels + * @param height in pixels + * @param captureType 0 = monochrome, 1 = rgb color + * @return number of pixels read or negative for error during read (e.g. size doesn't match frame size) + */ + + public int readFrame(int width, int height, int captureType) + { + int bytesPerPixel = 1; + int readLength = 0; + int rxImageSize; + int rgbShiftSize = 16; + int pixel = 255 << 24; + + if (captureType == 1) // if rgb color, 3 bytes/pixel, otherwise 1 for grayscale + bytesPerPixel = 3; + frameSize = width * height * bytesPerPixel; + + int maxReadSize = frameSize; + readBuf = new byte[frameSize]; // analysis form - temp buffer for raw received data + vidBuf = new byte[frameSize]; // raw image bytes + vidDispBuf = new int[frameSize]; // display version (alpha set, bytes replicated if needed) + int vidDispBufIx = 0; + int vidBufIx = 0; + + readBuf[0] = (byte)(200 + captureType); // send the "capture" command + byte[] t = new byte[4]; + + try { + out.write(readBuf, 0, 1); + out.flush(); + + readLength = in.read(t, 0, 4); // read imgWidth + if (readLength != 4) + return -4; + //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]); + imgWidth = ((t[2] << 8) & 0xff00) | t[3] & 0xff; // I think this way of getting byte to int is broken + if (imgWidth != width) + return -1; + + readLength = in.read(t, 0, 4); // read imgHeight + //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]); + if (readLength != 4) + return -5; + imgHeight = (int)(t[2] & 0xff) << 8 | t[3] & 0xff; + //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]); + if (imgHeight != height) + return -2; + + readLength = in.read(t, 0, 4); // read imgLength + if (readLength != 4) + return -6; + rxImageSize = ((t[1] << 16) & 0xff0000) | ((t[2] << 8) & 0xff00) | (int)t[3] & 0xff; + //System.out.println(t[0] + " " + t[1] + " " + t[2] + " " + t[3]); + if (rxImageSize != frameSize) + return -3; + + //System.out.println("Chumby reports image size " + imgWidth + "x" + imgHeight + " length " + rxImageSize + " for frameSize " + frameSize); + + + // ACHTUNG - readBuf gets overwritten at the beginning by multiple read buffers - use vidDispBuf or vidBuf for image + + for (int i=0; i<50; i++) { + readLength = in.read(readBuf, 0, maxReadSize); + maxReadSize -= readLength; + for (int j=0; j minLineThickness & create a LineBoundary for each + blackStart = blackEnd = 0; + for (int i=0; i minLineThickness) && ((blackEnd - blackStart) < maxLineThickness)) { + LineBoundary lb = new LineBoundary(); + lb.start = blackStart - (imgWidth/2); + lb.end = blackEnd - (imgWidth/2); + lb.middle = ((lb.start + lb.end)/2); + lineList.add(lb); + } // else ignore this as a false line (noise) - we're in white now + } + } else { // inWhite + if (smoothSlice[i] == 0) { + inBlack = true; // were in white, just transitioned to black (can happen on element 0) + blackStart = i; + } + } + } + + // print found lines +// System.out.print("Found " + lineList.size() + " lines: "); +// for (LineBoundary l : lineList) { +// System.out.print(l.start + " " + l.middle + " " + l.end + " "); +// } +// System.out.println(); + + return(0); + } + + /* + * Find the line we should be following. The very first time this runs, or if it loses the line & backs up + * it will pick the line closest to center. Thereafter it chooses the first found line who's center is within + * the boundaries of the last line it chose. If it can't find one, it returns an error. Therefore it will always take + * a left fork. It returns the middle value of the chosen line, or a large value if error. + */ + public int trackLine() + { + int m; + LineBoundary lTmp = new LineBoundary(); + // clear out current markers in the tracking display + m = currentLine.middle + imgWidth/2; + ltBuf[m + 3*imgWidth] = ltBuf[m + 2*imgWidth] = ltBuf[m + imgWidth] = ltBuf[m] = 0; + + if (lineList.size() == 0) { + if (currentLine.middle >= 0) { + System.out.println("Error: line disappeared to right, last seen at " + currentLine.middle); + return(100); + } else { + System.out.println("Error: line disappeared to left, last seen at " + currentLine.middle); + return (-100); + } + } + if (first == 1) { + lTmp.middle = 100; // any found line will be closer than this + for (LineBoundary lb : lineList) { + if (Math.abs(lb.middle) < Math.abs(lTmp.middle)) { + lTmp = lb; // save the new lineBoundary with the lowest absolute value of middle + } + } + currentLine = lTmp; + first = 0; + System.out.print("Picked line center at " + lTmp.middle); + return(lTmp.middle); + } else { + if (lineList.size() == 1) { + System.out.println("tracking line center at " + lineList.get(0).middle + " width: " + (lineList.get(0).end-lineList.get(0).start)); + currentLine = lineList.get(0); + return (lineList.get(0).middle); + } + for (LineBoundary lb : lineList) { + if ((lb.middle > currentLine.start-20) && (lb.middle < currentLine.end + 20)) { + System.out.println("tracking line center at " + lb.middle + " width: " + (lb.end-lb.start)); + currentLine = lb; + // write the image of the new currentLine middle + m = lb.middle+(imgWidth/2); + ltBuf[m] = 0xff<<24 | 0xff<<16; + ltBuf[m + 3*imgWidth] = ltBuf[m + 2*imgWidth] = ltBuf[m + imgWidth] = ltBuf[m]; + return (lb.middle); + } + } + } + System.out.println("Error: Lost the line I was tracking"); + first = 1; + return(2001); + } + public void testQuantization() + { + byte[] testArray1 = + {6, 6, 6, 10, + 6, 6, 6, 10, + 17, 17, 17, 17, + 17, 17, 17, 88}; + for (int i=0; i + * Run it with something like:
+ *   java roombacomm.ListSerialPorts
+ *  
+ * + */ +public class ListSerialPorts { + + public static void main(String[] args) { + + RoombaComm roombacomm = new RoombaCommSerial(); + String portlist[]; + //roombacomm.debug = true; + + portlist = roombacomm.listPorts(); + System.err.println("Available ports:"); + for(int i=0;i + Run it with something like:
+  java roombacomm.LogoA /dev/cu.KeySerial1
+ Usage: + roombacomm.LogoA serialportname [protocol] [options]
+ where: + protocol (optional) is SCI or OI + [options] can be one or more of: + -debug -- turn on debug output +
+ +*/ +public class LogoA { + + static String usage = + "Usage: \n"+ + " roombacomm.LogoA [options]\n" + + "where protocol (optional) is SCI or OI\n"+ + "[options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + "\n"; + static boolean debug = false; + + public static void main(String[] args) { + if( args.length == 0 ) { + System.out.println( usage ); + System.exit(0); + } + + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(); + for( int i=1; i < args.length; i++ ) { + if (args[i].equals("SCI") || (args[i].equals("OI"))) { + roombacomm.setProtocol(args[i]); + } else if( args[i].endsWith("debug") ) + debug = true; + } + + roombacomm.debug = debug; + + if( ! roombacomm.connect( portname ) ) { + System.out.println("Couldn't connect to "+portname); + System.exit(1); + } + + System.out.println("Roomba startup"); + roombacomm.startup(); + roombacomm.control(); + roombacomm.pause(30); + + for( int i=0; i<8; i++ ) { + roombacomm.spinRight( 45 ); + square( roombacomm, 100 ); + } + + System.out.println("Disconnecting"); + roombacomm.disconnect(); + + System.out.println("Done"); + } + + /** + * Make a square with a Roomba. + * Leaves Roomba in same place it began (theoretically) + * @param rc RoombaComm object connected to a Roomba + * @param size size of square in mm + */ + public static void square(RoombaComm rc, int size) { + rc.goForward( size ); + rc.spinLeft( 90 ); + rc.goForward( size ); + rc.spinLeft( 90 ); + rc.goForward( size ); + rc.spinLeft( 90 ); + rc.goForward( size ); + rc.spinLeft( 90 ); + } +} + diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Note.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Note.java new file mode 100644 index 0000000..d0bbc25 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Note.java @@ -0,0 +1,21 @@ + +package com.hackingroomba.roombacomm; + +/** + * Simple wrapper for musical notes + */ +public class Note { + public int notenum; // midi note number + public int duration; // in milliseconds + + Note( int anotenum, int aduration ) { + notenum = anotenum; + duration = aduration; + } + public String toString() { + return "("+notenum+","+duration+")"; + } + public int toSec64ths() { + return duration * 64/1000; + } +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Pid.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Pid.java new file mode 100644 index 0000000..97c5297 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Pid.java @@ -0,0 +1,67 @@ +package com.hackingroomba.roombacomm; + +/* + * This class implements the following PID algorithm + * previous_error = 0 + * start: + * error = setpoint - actual_position + * P = Kp * error + * I = Ki * sum(error) + * D = Kd * (error - previous_error) + * output = P + I + D + * previous_error = error + * wait(dt) + * goto start + */ +public class Pid { + double k_p, k_i, k_d, i_state_max; // the PID constants + double d_state, i_state; // the PID states + boolean disableD = true; + int lastError; + + Pid(double p, double i, double d) + { + k_p = p; + k_i = i; + k_d = d; + d_state = 0.0; + i_state = 0.0; + i_state_max = 200.0; + } + + public double computePid( double target, double value ) + { + double error; + double p, i, d; + double ret; + + error = target - value; + p = k_p * error; + d = k_d * (error - d_state ); + if (disableD) { // prevent an initial kick on the first iteration, before d_state is set. + disableD = false; + d = 0; + } + d_state = error; + i_state += error; + + // cap I term windup + if( i_state > i_state_max ) + i_state = i_state_max; + if( i_state < -i_state_max ) + i_state = -i_state_max; + // clear I term & diable D if we overshoot + if (((error > 0) && (lastError < 0)) || ((error < 0) && (lastError > 0))) { + i_state = 0; + disableD = true; + } + + i = k_i * i_state; + + ret = p + i + d; + System.out.printf("error %4.1f p: %4.1f i: %4.1f d: %4.1f\n", error, p, i, d); + + return ret; + } + +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLParser.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLParser.java new file mode 100644 index 0000000..9759b93 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLParser.java @@ -0,0 +1,112 @@ + + + +package com.hackingroomba.roombacomm; + +import java.util.*; +import java.util.regex.*; + +/** + * + */ +public class RTTTLParser { + + public static HashMap noteToNum; + static { + noteToNum = new HashMap(); + noteToNum.put("c", new Integer(0) ); + noteToNum.put("c#", new Integer(1) ); + noteToNum.put("d", new Integer(2) ); + noteToNum.put("d#", new Integer(3) ); + noteToNum.put("e", new Integer(4) ); + noteToNum.put("f", new Integer(5) ); + noteToNum.put("f#", new Integer(6) ); + noteToNum.put("g", new Integer(7) ); + noteToNum.put("g#", new Integer(8) ); + noteToNum.put("a", new Integer(9) ); + noteToNum.put("a#", new Integer(10) ); + noteToNum.put("b", new Integer(11) ); + noteToNum.put("h", new Integer(7) ); + } + + public static void main(String[] args) { + if( args.length == 0 ) { + System.out.println( "usage: roombacomm.RTTTLParser "); + System.exit(0); + } + String rtttl = args[0]; + ArrayList notelist = parse( rtttl ); + for( int i=0; i< notelist.size(); i++ ) { + System.out.println("notelist["+i+"]="+notelist.get(i)); + } + } + + public static ArrayList parse(String rtttl) { + System.out.println("parsing: "+rtttl); + String rtttl_working = rtttl.toLowerCase(); + String parts[] = rtttl_working.split(":"); + String name = parts[0]; + String defaults[] = parts[1].split("[,=]"); + String notes[] = parts[2].split(","); + + // global defaults + int bpm = 63; + int octave = 6; + int duration = 4; + + ArrayList notelist = new ArrayList(); + + for( int i=0; i < defaults.length; i++ ) { + //System.out.println("defaults["+i+"]="+defaults[i]); + if( defaults[i].equals("b") ) + try { bpm = Integer.parseInt(defaults[i+1]); } + catch(Exception e) {} + else if( defaults[i].equals("o") ) + try { octave = Integer.parseInt(defaults[i+1]); } + catch(Exception e) {} + else if( defaults[i].equals("d") ) + try { duration = Integer.parseInt(defaults[i+1]); } + catch(Exception e) {} + } + System.out.println("bpm:"+bpm+",octave:"+octave+",duration:"+duration); + + for( int i=0; i < notes.length; i++ ) { + Matcher m =Pattern.compile("(\\d+)*(.+?)(\\d)*(\\.)*").matcher(notes[i]); + m.find(); + // group(1) == duration (optional) + // group(2) == note (required) + // group(3) == scale (optional) + // group(4) == triplet (optional) + int dur = duration; + int oct = octave; + if( m.group(1) != null ) + try { dur = Integer.parseInt( m.group(1) ); } + catch(Exception e) {} + if( m.group(4) != null && m.group(4).equals(".") ) + dur += dur/2; + if( m.group(3) != null ) + try { oct = Integer.parseInt( m.group(3) ); } + catch(Exception e) {} + if( m.group(2) != null ) { + int notenum; + if( m.group(2).equals("p") ) { + notenum = 0; + } + else { + Integer nn = (Integer) noteToNum.get( m.group(2) ); + notenum = nn.intValue(); + notenum = notenum + 12*oct; + } + dur = bpmToMillis(bpm) / dur; + notelist.add( new Note( notenum, dur ) ); + } + } + return notelist; + } + + public static int bpmToMillis( int bpm ) { + return (60 * 1000 ) / bpm; + } +} + + diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLPlay.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLPlay.java new file mode 100644 index 0000000..7184022 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RTTTLPlay.java @@ -0,0 +1,144 @@ +/* + * roombacomm.RTTTLPlay + * + * Copyright (c) 2005 Tod E. Kurt, tod@todbot.com + * + * 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 com.hackingroomba.roombacomm; + +import java.util.*; + +/* + Play RTTL formatted ringtones on the Roomba. +

+ Run it with something like:

+  java roombacomm.RTTTLPlay /dev/cu.KeySerial1 'tron:d=4,o=5,b=200:8f6,8c6,8g,e,8p,8f6,8c6,8g,8f6,8c6,8g,e,8p,8f6,8c6,8g,e.,2d'
+ Usage: + roombacomm.RTTTLPlay serialportname [protocol] rttl_string [options]
+ where: + protocol (optional) is SCI or OI + rttl_string is a string of notes + [options] can be one or more of: + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -nohwhandshake -- don't use hardware-handshaking + -flush -- flush on sends(), normally not needed +
+ */ +public class RTTTLPlay { + + static String usage = + "Usage: \n"+ + " roombacomm.RTTTLPlay [protocol] [options]\n" + + "where:\n"+ + "protocol (optional) is SCI or OI\n"+ + "rttl string is a string of notes\n"+ + "[options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + " -nohwhandshake -- don't use hardware-handshaking\n"+ + " -flush -- flush on sends(), normally not needed\n"+ + "\n"; + static boolean debug = false; + static boolean hwhandshake = false; + static boolean flush = false; + + public static void main(String[] args) { + if( args.length < 2 ) { + System.out.println( usage ); + System.exit(0); + } + + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(); + int argOffset = 0; + if (args[1].equals("SCI") || (args[1].equals("OI"))) { + roombacomm.setProtocol(args[1]); + argOffset = 1; + } + String rtttl = args[1+argOffset]; + + for( int i=2+argOffset; i < args.length; i++ ) { + if( args[i].endsWith("debug") ) + debug = true; + else if( args[i].endsWith("nohwhandshake") ) + roombacomm.setWaitForDSR(false); + else if( args[i].endsWith("hwhandshake") ) + roombacomm.setWaitForDSR(true); + else if( args[i].endsWith("flush") ) + flush = true; + } + + + roombacomm.debug = debug; + roombacomm.flushOutput = flush; + + if( ! roombacomm.connect( portname ) ) { + System.out.println("Couldn't connect to "+portname); + System.exit(1); + } + + System.out.println("Roomba startup on port"+portname); + roombacomm.startup(); + roombacomm.control(); + roombacomm.pause(30); + + System.out.println("Checking for Roomba... "); + if( roombacomm.updateSensors() ) + System.out.println("Roomba found!"); + else + System.out.println("No Roomba. :( Is it turned on?"); + + ArrayList notelist = RTTTLParser.parse( rtttl ); + int songsize = notelist.size(); + // if within the size of a roomba song, make the nsong, then play + if( songsize <= 16 ) { + System.out.println("creating a song with createSong()"); + int notearray[] = new int[songsize*2]; + int j=0; + for( int i=0; i< songsize; i++ ) { + Note note = (Note) notelist.get(i); + int sec64ths = note.duration * 64/1000; + notearray[j++] = note.notenum; + notearray[j++] = sec64ths; + } + roombacomm.createSong( 1, notearray ); + roombacomm.playSong( 1 ); + } + // otherwise, try to play it in realtime + else { + System.out.println("playing song in realtime with playNote()"); + int fudge = 20; + for( int i=0; i< songsize; i++ ) { + Note note = (Note) notelist.get(i); + int duration = note.duration; + int sec64ths = duration*64/1000; + if( sec64ths < 5 ) sec64ths = 5; + if( note.notenum != 0 ) + roombacomm.playNote( note.notenum, sec64ths ); + roombacomm.pause( duration + fudge ); + } + } + System.out.println("Disconnecting"); + roombacomm.disconnect(); + + System.out.println("Done"); + } +} + diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmAPI.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmAPI.java new file mode 100644 index 0000000..65816f9 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmAPI.java @@ -0,0 +1,945 @@ +package com.hackingroomba.roombacomm; + +import java.io.*; +import java.net.*; +import java.awt.*; +import java.awt.image.*; +import java.util.*; +import java.net.URL; + +class RoboRealmAPI +{ + // default read and write socket timeout + public final static int DEFAULT_TIMEOUT = 60000; + + // the port number to listen on ... needs to match that used in RR interface + public final static int SERVER_PORTNUM = 6060; + + // indicates that the application is connected to RoboRealm Server + boolean connected = false; + + // holds the previously read data size + int lastDataTop = 0; + + // holds the previously read data buffer + int lastDataSize = 0; + + // contains the read/write socket timeouts + int timeout = DEFAULT_TIMEOUT; + + // general buffer for data manipulation and socket reading + byte buffer[] = new byte[4096]; + + // our instance of our primitive XML parser + XML xml = new XML(); + + // socket based reader and writer objects + BufferedInputStream bufferedReader; + BufferedOutputStream bufferedWriter; + + // out main socket handle + Socket handle; + + public int width=0, height=0; + + /******************************************************************************/ + /* Text string manipulation routines */ + /******************************************************************************/ + + public RoboRealmAPI() + { + } + + /* + Generalized string replace routine used in escaping strings + to the appropriate XML string. Java only has the character + replace routine as apposed to string replace. + */ + private String replace(String txt, String src, String dest) + { + if (txt==null) return new String(""); + int i,j; + int len=src.length(); + StringBuffer sb=new StringBuffer(txt.length()); + + j=0; + while ((i=txt.indexOf(src,j))>=0) + { + sb.append(txt.substring(j,i)); + sb.append(dest); + i+=len; + j=i; + } + sb.append(txt.substring(j)); + + return sb.toString(); + } + + /* + Escapes strings to be included in XML message. This can be accomplished by a + sequence of replace statements. + & -> & + " -> "e; + < -> < + > -> > + */ + private String escape(String txt) + { + txt = replace(txt, "&", "&"); + txt = replace(txt, "\"", ""e;"); + txt = replace(txt, "<", "<"); + txt = replace(txt, ">", ">"); + return txt; + } + + /******************************************************************************/ + /* Socket Routines */ + /******************************************************************************/ + + /* Initiates a socket connection to the RoboRealm server */ + public boolean connect(String hostname) + { + connected=false; + + try + { + handle = new Socket(hostname, SERVER_PORTNUM); + + handle.setSoTimeout(timeout); + + bufferedReader = new BufferedInputStream(handle.getInputStream()); + bufferedWriter = new BufferedOutputStream(handle.getOutputStream()); + } + catch (IOException e2) + { + //Unable to open connection to RoboRealm port 6060 + return false; + } + + connected=true; + + return true; + } + + /* close the socket handle */ + public void disconnect() + { + try + { + if (connected) + handle.close(); + } + catch (IOException e) + { + } + } + + // cause the roborealm application to close + public boolean close() + { + if (!connected) return false; + + if (send("")) + { + // read in variable length + String buffer; + if ((buffer = readMessage())!=null) + { + return buffer.equals("ok"); + } + } + + return false; + } + + // sends a String over the socket port to RoboRealm + private boolean send(String txt) + { + try + { + bufferedWriter.write(txt.getBytes(), 0, txt.length()); + bufferedWriter.flush(); + } + catch (IOException e) + { + return false; + } + return true; + } + + /* + Buffered socket image read. Since we don't know how much data was read from a + previous socket operation we have to add in any previously read information + that may still be in our buffer. We detect the end of XML messages by the + tag but this may require reading in part of the image data that + follows a message. Thus when reading the image data we have to move previously + read data to the front of the buffer and continuing reading in the + complete image size from that point. + */ + + public int readImageData(byte pixels[], int len) + { + int num; + + // check if we have any information left from the previous read + num = lastDataSize-lastDataTop; + if (num>len) + { + System.arraycopy(pixels, lastDataTop, buffer, 0, len); + lastDataTop+=num; + return num; + } + System.arraycopy(pixels, lastDataTop, buffer, 0, num); + len-=num; + lastDataSize=lastDataTop=0; + + // then keep reading until we're read in the entire image length + do + { + int res; + try + { + res = bufferedReader.read(pixels, num, len); + } + catch (IOException e) + { + return 0; + } + + if (res<0) + { + lastDataSize=lastDataTop=0; + return -1; + } + num+=res; + len-=res; + } + while (len>0); + + return num; + } + + /* If an image is too large for the provided buffer the rest of the data needs + to be skipped so we can continue to interact with the XML API. This routine + will remove that additional data from the socket*/ + public int skipData(int len) + { + int num; + + // check if we have any information left from the previous read + num = lastDataSize-lastDataTop; + if (num>len) + { + lastDataTop+=num; + return num; + } + len-=num; + lastDataSize=lastDataTop=0; + + try + { + bufferedReader.skip(len); + } + catch (IOException e) + { + return 0; + } + + return num+len; + } + + /* Read's in an XML message from the RoboRealm Server. The message is always + delimited by a tag. We need to keep reading in information until + this tag is seen. Sometimes this will accidentally read more than needed + into the buffer such as when the message is followed by image data. We + need to keep this information for the next readImage call.*/ + private String readMessage() + { + int num=0; + byte delimiter[] = "".getBytes(); + int top=0; + int i; + + // read in blocks of data looking for the delimiter + while (true) + { + int res; + try + { + res = bufferedReader.read(buffer, num, 4096-num); + } + catch (IOException e) + { + System.out.println(e.getMessage()); + return null; + } + + if (res<0) + { + lastDataSize=lastDataTop=0; + return null; + } + + lastDataSize=num+res; + for (i=num;i=delimiter.length) + { + num=i+1; + buffer[num]=0; + lastDataTop=num; + return new String(buffer, 0, num); + } + } + else + top=0; + } + num+=res; + } + } + + /******************************************************************************/ + /* API Routines */ + /******************************************************************************/ + + /* Returns the current image dimension */ + public Dimension getDimension() + { + if (!connected) return null; + + if (send("")) + { + // read in variable length + String buffer; + if ((buffer = readMessage())!=null) + { + if (xml.parse(buffer)) + { + return new Dimension(xml.getInt("response.width"), xml.getInt("response.height")); + } + } + } + + return null; + } + + /* + Returns the current processed image as a Java image. + */ + + public int[] getImage(String name) + { + if (!connected) return null; + if (name==null) name=""; + + // create the message request + if (send(""+escape(name)+"")) + { + String buffer; + // read in response which contains image information + if ((buffer=readMessage())!=null) + { + // parse image width and height + xml.parse(buffer); + int len = xml.getInt("response.length"); + width = xml.getInt("response.width"); + height = xml.getInt("response.height"); + // ensure that we have enough room in pixels + byte pixels[] = new byte[len]; + // actual image data follows the message + if (readImageData(pixels, len)==len) + { + //DataBuffer db = new DataBufferByte(pixels, width*height*3, 0); + //WritableRaster raster = Raster.createWritableRaster(BufferedImage.TYPE_3BYTE_BGR, db, null); + //return new BufferedImage(ColorModel.getRGBdefault(), raster, false, null); + int pixelInts[] = new int[width*height]; + int l = width*height*3; + int i,j; + for (j=i=0;i"+escape(name)+"")) + { + String buffer; + // read in response which contains image information + if ((buffer=readMessage())!=null) + { + // parse image width and height + xml.parse(buffer); + int len = xml.getInt("response.length"); + int width = xml.getInt("response.width"); + int height = xml.getInt("response.height"); + // ensure that we have enough room in pixels + if (len>max) + { + skipData(len); + return null; + } + + // actual image data follows the message + if (readImageData(pixels, len)==len) + return new Dimension(width, height); + } + } + + return null; + } + + /* + Sets the current source image. + pixels - input - contains RGB 8 bit byte. + width - input - contains grabbed image width + height - input - contains image height + */ + + public boolean setImage(byte pixels[], int width, int height) + { + return setImage(null, pixels, width, height); + } + + /* + Sets the current source image. + name - input - the name of the image to set. Can be source or marker name + pixels - input - contains RGB 8 bit byte. + width - input - contains grabbed image width + height - input - contains image height + */ + + public boolean setImage(String name, byte pixels[], int width, int height) + { + if (!connected) return false; + if (name==null) name=""; + + // setup the message request + if (send(""+escape(name)+""+width+"" + +height+"RGB1")) + { + // send the RGB triplet pixels after message + try + { + bufferedWriter.write(pixels, 0, width*height*3); + } + catch (IOException e) + { + return false; + } + + // read message response + String buffer; + if ((buffer = readMessage())!=null) + { + if (buffer.equals("ok")) + return true; + } + } + return false; + } + + /* + Returns the value of the specified variable. + name - input - the name of the variable to query + result - output - contains the current value of the variable + max - input - the maximum size of what the result can hold + */ + + public String getVariable(String name) + { + if (!connected) return null; + if ((name==null)||(name.length()==0)) return null; + + if (send(""+escape(name)+"")) + { + // read in variable length + String buffer; + if ((buffer=readMessage())!=null) + { + if (xml.parse(buffer)) + { + return xml.getFirst(); + } + } + } + + return null; + } + + /* + Returns the value of the specified variables. + name - input - the names of the variable to query + result - output - contains the current values of the variables + max - input - the maximum size of what the result can hold + */ + + public Vector getVariables(String names) + { + if (!connected) return null; + if ((names==null)||(names.length()==0)) return null; + + if (send(""+escape(names)+"")) + { + String buffer; + if ((buffer=readMessage())!=null) + { + return xml.parseVector(buffer); + } + } + + return null; + } + + /* + Sets the value of the specified variable. + name - input - the name of the variable to set + value - input - contains the current value of the variable to be set + */ + + public boolean setVariable(String name, String value) + { + if (!connected) return false; + if ((name==null)||(name.length()==0)) return false; + + if (send(""+escape(name)+""+escape(value)+"")) + { + // read in confirmation + String buffer; + if ((buffer=readMessage())!=null) + { + if (buffer.equals("ok")) + return true; + } + } + + return false; + } + + /* + Sets the value of the specified variables. + names - input - the name of the variable to set + values - input - contains the current value of the variable to be set + */ + + public boolean setVariables(String names[], String values[], int num) + { + if (!connected) return false; + if ((names==null)||(values==null)||(names[0].length()==0)) return false; + + int j=0; + int i; + + StringBuffer sb = new StringBuffer(); + + // create request message + sb.append(""); + for (i=0;(i"); + sb.append(escape(names[i])); + sb.append(""); + sb.append(escape(values[i])); + sb.append(""); + } + sb.append(""); + + // send that message to RR Server + if (send(sb.toString())) + { + // read in confirmation + String buffer; + if ((buffer=readMessage())!=null) + { + if (buffer.equals("ok")) + return true; + } + } + + return false; + } + + /* + Deletes the specified variable + name - input - the name of the variable to delete + */ + + public boolean deleteVariable(String name) + { + if (!connected) return false; + if ((name==null)||(name.length()==0)) return false; + + if (send(""+escape(name)+"")) + { + // read in variable length + String buffer; + if ((buffer=readMessage())!=null) + { + if (buffer.equals("ok")) + return true; + } + } + + return false; + } + + /* + Executes the provided image processing pipeline + source - the XML .robo file string + */ + + public boolean execute(String source) + { + if (!connected) return false; + if ((source==null)||(source.length()==0)) return false; + + //send the string + if (send(""+escape(source)+"")) + { + // read in result + String buffer; + if ((buffer=readMessage())!=null) + { + if (buffer.equals("ok")) + return true; + } + } + return false; + } + + /* + Executes the provided .robo file. Note that the file needs to be on the machine + running RoboRealm. This is similar to pressing the 'open program' button in the + main RoboRealm dialog. + filename - the XML .robo file to run + */ + public boolean loadProgram(String filename) + { + if (!connected) return false; + if ((filename==null)||(filename.length()==0)) return false; + + if (send(""+escape(filename)+"")) + { + String buffer; + if ((buffer=readMessage())!=null) + { + if (buffer.equals("ok")) + return true; + } + } + + return false; + } + + /* + Loads an image into RoboRealm. Note that the image needs to exist + on the machine running RoboRealm. The image format must be one that + RoboRealm using the freeimage.dll component supports. This includes + gif, pgm, ppm, jpg, png, bmp, and tiff. This is + similar to pressing the 'load image' button in the main RoboRealm + dialog. + name - name of the image. Can be "source" or a marker name, + filename - the filename of the image to load + */ + public boolean loadImage(String name, String filename) + { + if (!connected) return false; + + if ((filename==null)||(filename.length()==0)) return false; + if ((name==null)||(name.length()==0)) name="source"; + + if (send(""+escape(filename)+""+escape(name)+"")) + { + String buffer; + if ((buffer=readMessage())!=null) + { + if (buffer.equals("ok")) + return true; + } + } + + return false; + } + + /* + Saves the specified image in RoboRealm to disk. Note that the filename is relative + to the machine that is running RoboRealm. The image format must be one that + RoboRealm using the freeimage.dll component supports. This includes + gif, pgm, ppm, jpg, png, bmp, and tiff. This is + similar to pressing the 'save image' button in the main RoboRealm + dialog. + name - name of the image. Can be "source","processed", or a marker name, + filename - the filename of the image to save + */ + public boolean saveImage(String source, String filename) + { + if (!connected) return false; + + if ((filename==null)||(filename.length()==0)) return false; + if ((source==null)||(source.length()==0)) source="processed"; + + // create the save image message + if (send(""+escape(filename)+""+escape(source)+"")) + { + String buffer; + if ((buffer=readMessage())!=null) + { + if (buffer.equals("ok")) + return true; + } + } + + return false; + } + + /* + Sets the current camera driver. This can be used to change the current viewing camera + to another camera installed on the same machine. Note that this is a small delay + when switching between cameras. The specified name needs only to partially match + the camera driver name seen in the dropdown picklist in the RoboRealm options dialog. + For example, specifying "Logitech" will select any installed Logitech camera including + "Logitech QuickCam PTZ". + */ + public boolean setCamera(String name) + { + if (!connected) return false; + if ((name==null)||(name.length()==0)) return false; + + // create the save image message + if (send(""+escape(name)+"")) + { + String buffer; + if ((buffer=readMessage())!=null) + { + if (buffer.equals("ok")) + return true; + } + } + + return false; + } + + /* + This routine provides a way to stop processing incoming video. Some image processing + tasks can be very CPU intensive and you may only want to enable processing when + required but otherwise not process any incoming images to release the CPU for other + tasks. The run mode can also be used to processing individual frames or only run + the image processing pipeline for a short period. This is similar to pressing the + "run" button in the main RoboRealm dialog. + mode - can be toggle, on, off, once, or a number of frames to process + */ + public boolean run(String mode) + { + if (!connected) return false; + if ((mode==null)||(mode.length()==0)) return false; + + // create the save image message + if (send(""+escape(mode)+"")) + { + String buffer; + if ((buffer=readMessage())!=null) + { + if (buffer.equals("ok")) + return true; + } + } + + return false; + } + + /* + There is often a need to pause your own Robot Controller program to wait for + RoboRealm to complete its task. The eaisest way to accomplish this is to wait + on a specific variable that is set to a specific value by RoboRealm. Using the + waitVariable routine you can pause processing and then continue when a variable + changes within RoboRealm. + name - name of the variable to wait for + value - the value of that variable which will cancel the wait + timeout - the maximum time to wait for the variable value to be set + */ + + public boolean waitVariable(String name, String value, int timeout) + { + if (timeout==0) timeout=100000000; + + if (!connected) return false; + if ((name==null)||(name.length()==0)) return false; + + if (send(""+escape(name)+""+escape(value)+""+timeout+"")) + { + try + { + handle.setSoTimeout(timeout); + } + catch (SocketException e) + { + return false; + } + String buffer; + if ((buffer=readMessage())!=null) + { + try + { + handle.setSoTimeout(DEFAULT_TIMEOUT); + } + catch (SocketException e) + { + return false; + } + if (buffer.equals("ok")) + return true; + } + try + { + handle.setSoTimeout(DEFAULT_TIMEOUT); + } + catch (SocketException e) + { + return false; + } + } + + return false; + } + + /* + If you are rapdily grabbing images you will need to wait inbetween each + get_image for a new image to be grabbed from the video camera. The wait_image + request ensures that a new image is available to grab. Without this routine + you may be grabbing the same image more than once. + */ + + public boolean waitImage(int timeout) + { + if (!connected) return false; + + if (send(""+timeout+"")) + { + String buffer; + if ((buffer=readMessage())!=null) + { + if (buffer.equals("ok")) + return true; + } + } + + return false; + } + + /* If you are running RoboRealm on the same machine as your API program you can use + this routine to start RoboRealm if it is not already running. + filename - the path to RoboRealm on your machine + */ + + //////////////////////////////////// Basic Image Load/Save routines //////////////////////// + // Utility routine to save a basic PPM + public boolean savePPM(String filename, byte buffer[], int width, int height) + { + try + { + FileOutputStream fos = new FileOutputStream(new File(filename)); + String header = "P6\n"+width+" "+height+"\n255\n"; + fos.write(header.getBytes(), 0, header.length()); + fos.write(buffer, 0, width*height*3); + fos.close(); + } + catch (Exception e) + { + return false; + }; + + return true; + } + + private String readLine(FileInputStream fis) + { + StringBuffer sb = new StringBuffer(); + while (true) + { + try + { + int c = fis.read(); + if (c=='\n') + { + if (sb.charAt(0)!='#') + return sb.toString(); + + sb.setLength(0); + } + sb.append((char)c); + } + catch (Exception e) + { + return null; + } + } + } + + // Utility routine to load a basic PPM. Note that this routine does NOT handle + // comments and is only included as a quick example. + public Dimension loadPPM(String filename, byte buffer[], int max) + { + int width=0, height=0; + + try + { + FileInputStream fis = new FileInputStream(new File(filename)); + + // read in P6 header skipping comments + String header = readLine(fis); + if (!header.equals("P6")) return null; + + // read in width height header skipping comments + String size = readLine(fis); + int ind = size.indexOf(' '); + if (ind<0) return null; + width = Integer.parseInt(size.substring(0, ind)); + height = Integer.parseInt(size.substring(ind+1)); + + if ((width*height*3)>max) return null; + fis.read(buffer, 0, width*height*3); + fis.close(); + } + catch (Exception e) + { + return null; + }; + + return new Dimension(width, height); + } +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmTest.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmTest.java new file mode 100644 index 0000000..f38e736 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoboRealmTest.java @@ -0,0 +1,240 @@ +package com.hackingroomba.roombacomm; + +import java.awt.*; +import java.net.*; +import java.util.*; +import java.awt.image.*; +import java.io.*; +import java.net.URL; + +public class RoboRealmTest +{ + public static void sendImage(RoboRealmAPI rr, Image img) + { + int width = img.getWidth(null); + int height = img.getHeight(null); + + // create your own copy of the data in case the parent goes away ... + int pixelInt[] = new int[width*height]; + byte pixelByte[] = new byte[width*height*3]; + + // Get the image data + PixelGrabber grabber = new PixelGrabber(img, 0, 0, width, height, pixelInt, 0, width); + + try + { + grabber.grabPixels(); + + int i,j; + + for (j=i=0;i>8)&255); + pixelByte[j++] = (byte)((num>>16)&255); + } + + // send image to RR + rr.setImage(pixelByte, width, height); + } + catch (Exception e) + { + }; + } + + + // This is where the program first starts + public static void main(String[] args) + { + byte image[] = new byte[1280*960]; + RoboRealmAPI rr = new RoboRealmAPI(); + + if (!rr.connect("localhost")) + { + System.out.println("Could not connect to RoboRealm on localhost!"); + return; + } + + // load an image using Java awt + + Dimension d = rr.getDimension(); + if (d!=null) + { + System.out.println("Dimension "+d.width+"x"+d.height); + } + + // VARIABLES + + // set a custom variable to test + rr.setVariable("custom_var", "test"); + + // read back our custom variable ... should be equal to 'test' + String res = rr.getVariable("custom_var"); + if (!res.equals("test")) + { + System.out.println("Error in custom_var"); + return; + } + + // delete our custom variable + if (!rr.deleteVariable("custom_var")) + { + System.out.println("Error in delete variable"); + return; + } + + // try to get it back again ... should be empty + res = rr.getVariable("custom_var"); + if (res!=null) + { + System.out.println("Error in delete custom_var"); + return; + } + + // set multiple variables + String names[] = new String[2]; + String values[] = new String[2]; + names[0]="custom_var_1"; + names[1]="custom_var_2"; + values[0] = "test1"; + values[1] = "test2"; + rr.setVariables(names, values, 2); + + // get multiple variables + Vector v = rr.getVariables("custom_var_1, custom_var_2"); + if (v==null) + { + System.out.println("Error in GetVariables, did not return any results"); + return; + } + else + { + if (!((String)v.elementAt(0)).equals("test1")) + { + System.out.println("Error in get/set multiple variables. Got "+(String)v.elementAt(0)); + return; + } + if (!((String)v.elementAt(1)).equals("test2")) + { + System.out.println("Error in get/set multiple variables. Got "+(String)v.elementAt(1)); + return; + } + } + + // IMAGES + + // ensure that the camera is on and processing images + rr.setCamera("on"); + rr.run("on"); + + // execute a RGB filter on the loaded image + rr.execute("1.50403"); + + // get the current processed image from RoboRealm and save as a PPM + d = rr.getImage(image, 1280*960); + if (d!=null) + { + rr.savePPM("c:\\temp\\test.ppm", image, d.width, d.height); + } + + // get the current source image from RoboRealm and save as a PPM + d = rr.getImage("source", image, 1280*960); + if (d!=null) + { + rr.savePPM("c:\\temp\\test2.ppm", image, d.width, d.height); + } + + // turn off live camera + rr.setCamera("off"); + + // load an image for experimentation + d = rr.loadPPM("c:\\Program Files\\RoboRealm\\remo.ppm", image, 320*240*3); + + // change the current image + rr.setImage(image, d.width, d.height); + + // load an image using Java awt + try + { + Image img = Toolkit.getDefaultToolkit().getImage(new URL("http://www.google.com/intl/en_ALL/images/logo.gif")); + sendImage(rr, img); + } + catch (Exception e) + { + }; + + // add a marker image called my_new_image + rr.setImage("my_new_image", image, d.width, d.height); + + // run a .robo program + //rr.loadProgram("c:\\Program Files\\RoboRealm\\scripts\\red.robo"); + + // load an image from disk + rr.loadImage(null, "c:\\Program Files\\RoboRealm\\remo.gif"); + + // save that image back to disk .. note that we can switch extensions + rr.saveImage(null, "c:\\temp\\remo.jpg"); + + rr.setCamera("on"); + // change the camera to another one + //rr.setCamera("CompUSA PC Camera"); + try + { + Thread.sleep(2000); + } + catch (Exception e) + { + }; + // now set it back + //rr.setCamera("Logitech"); + + // turn off processing + rr.run("off"); + try + { + Thread.sleep(2000); + } + catch (Exception e) + { + }; + // run once + rr.run("once"); + try + { + Thread.sleep(2000); + } + catch (Exception e) + { + }; + // run for 100 frames (~3.3 seconds) .. note that if your frame rate is different this + // may be longer than 4 seconds + System.out.println("running for 100 frames"); + rr.run("100"); + try + { + Thread.sleep(4000); + } + catch (Exception e) + { + }; + // turn processing back on + rr.run("on"); + + // wait for the image count to exceed 1000 (assuming a 30 fps here) + System.out.println("waiting for image count to exceed 1000"); + rr.waitVariable("image_count", "500", 100000); + + // wait for a new image + System.out.println("waiting for a new image"); + rr.waitImage(5000); + + // close the RoboRealm application .. if you want too ... otherwise leave it running + //rr.close(); + + // disconnect from API Server + rr.disconnect(); + + System.out.println("Finished RoboRealmTest"); + } +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Roborama.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Roborama.java new file mode 100644 index 0000000..5fd4a40 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Roborama.java @@ -0,0 +1,910 @@ +/* + * roombacomm.Bsquare -- test out the Bsquare command + * + * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net + * + * 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 com.hackingroomba.roombacomm; + +import jargs.gnu.CmdLineParser; +import com.hackingroomba.roombacomm.RoombaCommTCPClient; +import java.io.*; + + +/** + Drive the Roomba forward, back, or CW or CCW square
+

+ Run it with something like:

+	    java roombacomm.Bsquare /dev/cu.KeySerial1 [protocol] command velocity distance
+ Usage: + roombacomm.Bsquare [protocol] [options]
+ where + protocol (optional) is SCI or OI + command is one of: + f -- forward or back; direction controlled by +/- speed + bcw or bccw -- clockwise or counter-clockwise b-square + onb -- out and back + [options] can be one or more of: + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -nohwhandshake -- don't use hardware-handshaking + velocity and distance are in inches\n"; +
+*/ + public class Roborama { + + private int maxLineWidth = 30; + private int minLineWidth = 5; + private int height = 120; + private int width = 160; + String usage = + "Usage: \n"+ + " roombacomm.Roborama [protocol] [options]\n" + + "where is one of:\n" + + " f: go forward or back based on velocity for the specified distance\n" + + " bcw: do a clockwise Borenstein square with leg size specified\n" + + " bccw: do a counter-clockwise Borenstein square with leg size specified\n" + + " onb: do out'n'back for distance specified\n" + + " fig8: do figure 8 with length specified (width is hard-coded to 3 feet)\n" + + " followline: Follow a line using camera" + + " followLine takes arguments like: -v 100 -p 192.168.15.150:5001 -c followLine --videoServer 192.168.15.150 --videoPortNum 5005 -x 320 -y 240 --debug" + + " to match roombasrvr string ./roombasrvr -v -x320 -y240 -m " + + "and where [options] can be one or more of:\n"+ + " --debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + "velocity and distance are in mm/s and mm\n"; + boolean debug = false; + boolean hwhandshake = false; + byte [] sensorBytes; // The last bytes read by roombacomm library + byte bumpSensor; + short odoDistance; + int leftEncoder, rightEncoder; + int leftMm, rightMm; + int beginLeft, beginRight; + private final static double countsPerMm = 2.57; + private int minVelocity = 50; + RoombaComm roombacomm; + private String portname; + private String cmd; + private int velocity; + private int waittime; + private String protocol; + private int distance = 0 ; + private int angle; + private int radius; + private int thresholdOverride = 0; + private String videoServer = ""; + private int videoPortNum = 0 ; + private int videoRowStart = 50; + private int videoRowEnd = 54; + + public Roborama() { + // constructor, mustn't throw exceptions. Do nothing for now + } + + // main - it all starts here + public static void main(String[] args) { + Roborama b = new Roborama(); + b.doCommand(args); + } + + public void f() + { + /* + // get sensor values before starting + roombacomm.queryList(sensorList, 6); + boolean sensorStatus = roombacomm.wait4Sensors(); + if (sensorStatus == false) { + System.out.println("Failed to read sensors"); // wait until sensorsValid true, when 6 bytes ready + System.exit(-1); + } + sensorBytes = roombacomm.getSensor_bytes(); + odoDistance = roombacomm.toShort(sensorBytes[0], sensorBytes[1]); + leftEncoder = roombacomm.toShort(sensorBytes[2], sensorBytes[3]); + rightEncoder = roombacomm.toShort(sensorBytes[4], sensorBytes[5]); + System.out.println("Odometry sensors: distance: " + odoDistance + " left encoder: " + + leftEncoder + " rightEncoder " + rightEncoder); +*/ + // now drive + roombacomm.drive( getVelocity(), 0x8000 ); + roombacomm.pause(getWaittime()); + roombacomm.stop(); + roombacomm.pause(250); // wait for robot to stop moving +/* + // get sensor values after the run + roombacomm.queryList(sensorList, 6); + sensorStatus = roombacomm.wait4Sensors(); + if (sensorStatus == false) { + System.out.println("Failed to read sensors"); // wait until sensorsValid true, when 6 bytes ready + System.exit(-1); + } + sensorBytes = roombacomm.getSensor_bytes(); + odoDistance = roombacomm.toShort(sensorBytes[0], sensorBytes[1]); + leftEncoder = roombacomm.toShort(sensorBytes[2], sensorBytes[3]); + rightEncoder = roombacomm.toShort(sensorBytes[4], sensorBytes[5]); + System.out.println("Odometry sensors: distance: " + odoDistance + " left encoder: " + + leftEncoder + " rightEncoder " + rightEncoder); +*/ + } + + void initOdometry() + { + beginLeft = beginRight = 0; + // get sensor values before starting + getOdometry(); // get 5 bytes of return data + beginLeft = leftEncoder; + beginRight = rightEncoder; + getOdometry(); // update leftMm & rightMm (should be 0) + printOdometry(); + } + boolean getOdometry() + { + byte[] sensorList = {7, 43, 44}; // bump/wheeldrop, and encoders + int returnLength = 5; + + roombacomm.queryList(sensorList, returnLength); + roombacomm.logmsg("updateSensors: pausing."); + + for(int i=0; i < 20; i++) { + if( roombacomm.sensorsValid() ) { + break; + } + roombacomm.pause( 50 ); + } + if (!roombacomm.sensorsValid()) { + System.out.println("ERROR: unable to read sensors"); + return(false); + } + sensorBytes = roombacomm.getSensor_bytes(); + bumpSensor = sensorBytes[0]; + leftEncoder = RoombaComm.toUnsignedShort(sensorBytes[1], sensorBytes[2]); + leftMm = (int)((leftEncoder - beginLeft) / countsPerMm); + rightEncoder = RoombaComm.toUnsignedShort(sensorBytes[3], sensorBytes[4]); + rightMm = (int)((rightEncoder - beginRight) / countsPerMm); + return(true); + } + void printOdometry() + { + System.out.println("Odometry sensors: left encoder: " + + leftEncoder + " (" + leftMm + "mm), rightEncoder " + rightEncoder + " (" + rightMm + "mm) bump: " + bumpSensor); + + } + public void fod() // forward using odometry + { + int rampDownDistance; + int currentVelocity = minVelocity; // starting velocity + long nextUpdateTime; + boolean rampUp, rampDown; + int currentRadius = radius; + int initialDifference; + int distanceTravelled; + + initOdometry(); + initialDifference = leftEncoder - rightEncoder; + rampDownDistance = (distance - 150); // - (int)(Math.pow(velocity, 2)/ 200); + System.out.println("target: " + distance + " rampdown Distance: " + rampDownDistance); + rampUp = true; + rampDown = false; + + // every 100ms calculate new speed & correct course + roombacomm.logmsg("velocity: " + velocity + " rampUp: " + rampUp + " rampDown: " + rampDown); + do { + nextUpdateTime = System.currentTimeMillis() + 100; + if (rampUp == true) { + currentVelocity += 20; + if (currentVelocity >= velocity) { + currentVelocity = velocity; + rampUp = false; + } + } + if (rampDown == true) { + currentVelocity -= 20; + if (currentVelocity < minVelocity) { + currentVelocity = minVelocity; + } + } + getOdometry(); + printOdometry(); + + // check whether we hit anything + if (bumpSensor != 0) { + System.out.println("Hit bumper - stopping"); + roombacomm.stop(); + roombacomm.powerOff(); + System.exit(-1); + } + + // Check which wheel to track for distance forward, or track average if not spinning + if (radius == 1) { + distanceTravelled = rightMm; + } else if (radius == -1) { + distanceTravelled = leftMm; + } else { + distanceTravelled = leftMm; + } + + if (distanceTravelled > rampDownDistance) { + rampUp = false; + rampDown = true; + } + + if (distanceTravelled > distance) + break; + + System.out.println("encoder difference: " + (leftEncoder - rightEncoder - initialDifference)); + //System.out.println("vel " + currentVelocity + " rampUp " + rampUp + " rampDown " + rampDown); + roombacomm.logmsg("driving at: " + currentVelocity + " radius: " + radius + " leftMm: " + leftMm); + roombacomm.drive( currentVelocity, radius ); + + // Sleep till next tick + if (System.currentTimeMillis() < nextUpdateTime) { + try { + Thread.sleep(nextUpdateTime - System.currentTimeMillis()); + } catch(Exception e) { + e.printStackTrace(); + } + } else { + System.out.println("WARNING: missed frame by " + (System.currentTimeMillis() - nextUpdateTime) + "ms"); + } + } while (distanceTravelled < distance); + + roombacomm.stop(); + roombacomm.pause(500); // wait for roomba to settle + + getOdometry(); // get 5 bytes of return data + printOdometry(); + + System.out.println("Ldistance: " + (leftEncoder - beginLeft) + " Rdistance: " + (rightEncoder - beginRight)); + + + } + + public void bcwod() + { + int tempDistance = distance; + int tempRadius = radius; + int turnDistance = angle; // use --angle to set turnDistance + + // left leg + distance = tempDistance; + radius = tempRadius; + fod(); + + radius = -1; + distance = turnDistance; + fod(); + + // top leg + distance = tempDistance; + radius = tempRadius; + fod(); + + radius = -1; + distance = turnDistance; + fod(); + + // right leg + distance = tempDistance; + radius = tempRadius; + fod(); + + radius = -1; + distance = turnDistance; + fod(); + + // bottom leg + distance = tempDistance; + radius = tempRadius; + fod(); + + radius = -1; + distance = turnDistance; + fod(); + + } + public void bcw() + { + roombacomm.drive( velocity, radius ); + roombacomm.pause(waittime); + roombacomm.stop(); + roombacomm.pause(1000); + roombacomm.spin(-angle); + roombacomm.pause(1000); + + roombacomm.drive( velocity, radius ); + roombacomm.pause(waittime); + roombacomm.stop(); + roombacomm.pause(1000); + roombacomm.spin(-angle); + roombacomm.pause(1000); + + roombacomm.drive( velocity, radius ); + roombacomm.pause(waittime); + roombacomm.stop(); + roombacomm.pause(1000); + roombacomm.spin(-angle); + roombacomm.pause(1000); + + roombacomm.drive( velocity, radius ); + roombacomm.pause(waittime); + roombacomm.stop(); + roombacomm.pause(1000); + roombacomm.spin(-angle); + } + + public void bccw() + { + roombacomm.drive( velocity, radius ); + roombacomm.pause(waittime); + roombacomm.stop(); + roombacomm.pause(1000); + roombacomm.spin(angle); + roombacomm.pause(1000); + + roombacomm.drive( velocity, radius ); + roombacomm.pause(waittime); + roombacomm.stop(); + roombacomm.pause(1000); + roombacomm.spin(angle); + roombacomm.pause(1000); + + roombacomm.drive( velocity, radius ); + roombacomm.pause(waittime); + roombacomm.stop(); + roombacomm.pause(1000); + roombacomm.spin(angle); + roombacomm.pause(1000); + + roombacomm.drive( velocity, radius ); + roombacomm.pause(waittime); + roombacomm.stop(); + roombacomm.pause(1000); + roombacomm.spin(angle); + } + + public void onbod() + { + fod(); + roombacomm.drive(-200, radius); + roombacomm.pause(500); + roombacomm.drive(-400, radius); + roombacomm.pause(500); + roombacomm.drive( 0-velocity, radius ); + roombacomm.pause(waittime+5000); + roombacomm.stop(); + roombacomm.pause(1500); + + } + public void onb() + { + roombacomm.drive(200, radius); + roombacomm.pause(500); + roombacomm.drive(400, radius); + roombacomm.pause(500); + roombacomm.drive( velocity, radius ); + roombacomm.pause(waittime); + roombacomm.stop(); + roombacomm.pause(500); + roombacomm.drive(-200, radius); + roombacomm.pause(500); + roombacomm.drive(-400, radius); + roombacomm.pause(500); + roombacomm.drive( 0-velocity, radius ); + roombacomm.pause(waittime); + roombacomm.stop(); + roombacomm.pause(1000); + //roombacomm.spin(360); + } + + public void fig8() + { + // calculate delay when crossing from one side of figure 8 to the other + int crosstime = Math.abs((1000 / velocity) * 900); // waittime in ms (correction .9) - 3' cross + + // lower left leg + System.out.println("lower left leg"); + fod(); + roombacomm.spin(-angle); + roombacomm.pause(500); + + // 1st middle crossing + System.out.println("1st middle crossing"); + fod(); + roombacomm.spin(angle); + roombacomm.pause(500); + + // upper right leg + System.out.println("upper right leg"); + fod(); + roombacomm.spin(angle); + roombacomm.pause(500); + + // upper crossing + System.out.println("upper crossing"); + fod(); + roombacomm.spin(angle); + roombacomm.pause(500); + + // upper left leg + System.out.println("upper left leg"); + fod(); + roombacomm.spin(angle); + roombacomm.pause(500); + + // 2nd middle crossing + System.out.println("2nd middle crossing"); + fod(); + roombacomm.spin(-angle); + roombacomm.pause(500); + + // lower right leg + System.out.println("lower right leg"); + fod(); + roombacomm.spin(-angle); + roombacomm.pause(500); + + // bottom crossing + System.out.println("bottom crossing"); + fod(); + roombacomm.spin(-angle); + roombacomm.pause(500); + } + + public void followLine() + { + int frameSize; + int trackError, trackErrorPrev1, trackErrorPrev2; + double loopGain = 5; + + if (getVideoServer() == null || getVideoServer().length() == 0) { + System.err.println(" you must supply a --videoServer value to use the command \"getVideo\""); + if (getVideoPortNum() <= 0) { + System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\""); + } + System.exit(6); + } + if (getVideoPortNum() <= 0) { + System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\""); + System.exit(7); + } + trackErrorPrev1 = 0; + trackErrorPrev2 = 0; + + FrameProcessor fp = new FrameProcessor(getVideoServer(),getVideoPortNum(), getWidth(), getHeight(), getMinLineWidth(), getMaxLineWidth(), getThresholdOverride()); + //fp.testQuantization(); + fp.createAndShowGUI(); + fp.connect(false); + + while (true) { + frameSize = fp.readFrame(getWidth(), getHeight(), 0); + if (frameSize != (getWidth() * getHeight())) { + System.out.println("getVideo read " + frameSize + " bytes - abandoning frame"); + continue; + } + if (fp.quantizeRows(videoRowStart, videoRowEnd) == 0) + fp.segmentImage(); + fp.displayFrame(); + + trackError = 0 - fp.trackLine(); // change error sign to correspond to desired turn direction + + // calculate drive radius - lots of magic numbers here + if (Math.abs(trackError)<5){ + radius = 0x8000; + } else if ((trackError >= 5) && (trackError < 74)){ + radius = (int)(-loopGain * trackError + 321.0); + if (radius < 1) + radius = 1; + } else if ((trackError <= -5) && trackError > -74) { + radius = (int)(-loopGain * trackError - 321); + if (radius > -1) + radius = -1; + } else if ((trackError == -100) || (trackError == 100)) { + roombacomm.stop(); + roombacomm.pause(1000); + roombacomm.drive(50, 0x8000); + roombacomm.pause(3240); // go forward 1/2 a roomba length + roombacomm.stop(); + roombacomm.pause(200); + roombacomm.spin((trackError < 0) ? -angle : angle); // spin 90 degrees + roombacomm.stop(); + roombacomm.pause(200); + roombacomm.drive(-50, 0x8000); // back up 1/2 a roomba length + roombacomm.pause(3240); + roombacomm.stop(); // camera should be on same point we lost the line + continue; + } else if (trackError == 2001) { + roombacomm.stop(); + roombacomm.pause(1000); + roombacomm.drive(-50, 0x8000); + roombacomm.pause(500); // go forward 1/2 a roomba length + roombacomm.stop(); + roombacomm.pause(200); + continue; + } else { + System.out.println("HELP: TRACKERROR OUT OF BOUNDS: " + trackError); + roombacomm.drive(0, 8000); // stop + continue; + } + roombacomm.logmsg(" Driving radius: " + radius); + roombacomm.drive(velocity, radius); + + } + //frameSize = fp.readFrame(); + //System.out.println("getVideo read " + frameSize + " bytes"); + //fp.disconnect(); + } + + public void normalize() + { + int frameSize; + FileOutputStream fos; + DataOutputStream dos; + + if (getVideoServer() == null || getVideoServer().length() == 0) { + System.err.println(" you must supply a --videoServer value to use the command \"getVideo\""); + if (getVideoPortNum() <= 0) { + System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\""); + } + System.exit(6); + } + if (getVideoPortNum() <= 0) { + System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\""); + System.exit(7); + } + + FrameProcessor fp = new FrameProcessor(getVideoServer(),getVideoPortNum(), getWidth(), getHeight(), getMinLineWidth(), getMaxLineWidth(), getThresholdOverride()); + fp.connect(false); + + frameSize = fp.readFrame(getWidth(), getHeight(), 0); + System.out.println("getVideo read " + frameSize + " bytes"); + double [] normalizeArray = fp.normalizeRows(videoRowStart, videoRowEnd); + + try { + File file= new File("normalizeArray"); + fos = new FileOutputStream(file); + dos=new DataOutputStream(fos); + for (int i=0; i=4\n"); + System.out.println( usage ); + System.exit(0); + } + parseCmd(args); + + // open a connection to Roomba (net or serial) + char portNameChar1 = getPortname().charAt(0); + if (portNameChar1 >= '0' && portNameChar1 <='9') { // portname begins with number, assume it's an IP + System.out.println("Using network IP " + getPortname()); + RoombaCommTCPClient roombacommTCPClient = new RoombaCommTCPClient(); +// roombacommTCPClient.setProtocol(args[1]); + roombacommTCPClient.setProtocol(getProtocol()); + if( ! roombacommTCPClient.connect( getPortname() ) ) { + System.out.println("Couldn't connect to "+getPortname()); + System.exit(1); + } + roombacomm = roombacommTCPClient; + } else { + System.out.println("using serial port " + getPortname()); + RoombaCommSerial roombacommSerial = new RoombaCommSerial(); + roombacommSerial.setProtocol(getProtocol()); + roombacommSerial.setWaitForDSR(isHwhandshake()); + if( ! roombacommSerial.connect( getPortname() ) ) { + System.out.println("Couldn't connect to "+getPortname()); + System.exit(1); + } + roombacomm = roombacommSerial; + } + + // set up ^C handling + MyShutdown sh = new MyShutdown(roombacomm); + Runtime.getRuntime().addShutdownHook(sh); + roombacomm.debug = isDebug(); + System.out.println("Roomba startup"); + roombacomm.startup(); + roombacomm.control(); +//over ride + // roombacomm.full(); + roombacomm.pause(100); + + // run the requested command + System.out.println("running command " + getCmd() + "\n"); + if (cmd.equals("f") ) { + f(); + } else if (cmd.equals("bccw")) { + bccw(); + } else if (cmd.equals("bcw")){ + bcw(); + } else if (cmd.equals("onb")) { + onb(); + } else if (cmd.equals("onbod")) { + onbod(); + } else if (cmd.equals("spin")) { + for (int i=0; i<4; i++) { + roombacomm.spin(-angle); + roombacomm.pause(1000); + } + } else if (cmd.equals("fig8")) { + fig8(); + } else if (cmd.equals("followLine")) { + followLine(); + } else if (cmd.equals("fod")){ // forward using odometry + fod(); + } else if (cmd.equals("bcwod")) { + bcwod(); + } else { + System.out.println("Invalid Command"); + } + roombacomm.stop(); + + System.out.println("Disconnecting"); + roombacomm.disconnect(); + + System.out.println("Done"); + } + + public void parseCmd(String[] args){ + System.out.println("*** start of parseCmd"); + + CmdLineParser parser = new CmdLineParser(); + CmdLineParser.Option debugOption = parser.addBooleanOption('X', "debug"); +// CmdLineParser.Option verboseOption = parser.addBooleanOption('W', "Verbose"); + CmdLineParser.Option portNameOption = parser.addStringOption('p', "portname"); + CmdLineParser.Option protocalOption = parser.addStringOption('a', "api"); + CmdLineParser.Option angleOption = parser.addIntegerOption("angle"); + CmdLineParser.Option velocityOption = parser.addIntegerOption('v', "velocity"); + CmdLineParser.Option distanceOption = parser.addIntegerOption('d', "distance"); + CmdLineParser.Option commandOption = parser.addStringOption('c', "command"); + CmdLineParser.Option radiusOption = parser.addIntegerOption('r', "radius"); + CmdLineParser.Option widthOption = parser.addIntegerOption('x', "width"); + CmdLineParser.Option heightOption = parser.addIntegerOption('y', "height"); + CmdLineParser.Option minlineOption = parser.addIntegerOption("min"); + CmdLineParser.Option maxlineOption = parser.addIntegerOption("max"); + CmdLineParser.Option videoServerOption = parser.addStringOption("videoServer"); + CmdLineParser.Option videoPortNumOption = parser.addIntegerOption("videoPortNum"); + CmdLineParser.Option thresholdOption = parser.addIntegerOption('t', "threshold"); + CmdLineParser.Option hwHandShakeOption = parser.addBooleanOption("nohwhandshake"); + try { + parser.parse(args); + } + catch ( CmdLineParser.OptionException e ) { + System.err.println(e.getMessage()); + System.out.println("parseCmd had an error\n"+ usage ); + System.exit(2); + } + + // String portname = args[0]; // e.g. "/dev/cu.KeySerial1", or "COM5" or "192.168.1.1" + setPortname((String)parser.getOptionValue(portNameOption)); + System.out.println("portname is ("+getPortname()+")"); + + setProtocol((String)parser.getOptionValue(protocalOption,"SCI")); + System.out.println("protocal is ("+getProtocol()+")"); + setAngle((Integer)parser.getOptionValue(angleOption,new Integer(83))); + System.out.println("angle is ("+getAngle()+")"); + setRadius((Integer)parser.getOptionValue(radiusOption,new Integer(0x8000))); + System.out.println("radius is ("+getRadius()+")"); + if (args[1].equals("SCI") || (args[1].equals("OI"))) { + } else { + } + setThresholdOverride(((Integer)parser.getOptionValue(thresholdOption, getThresholdOverride())).intValue()); + setWidth(((Integer)parser.getOptionValue(widthOption,getWidth())).intValue()); + setHeight(((Integer)parser.getOptionValue(heightOption,getHeight())).intValue()); + setMinLineWidth(((Integer)parser.getOptionValue(minlineOption,getMinLineWidth())).intValue()); + setMaxLineWidth(((Integer)parser.getOptionValue(maxlineOption,getMaxLineWidth())).intValue()); + setVideoServer(((String)parser.getOptionValue(videoServerOption))); + setVideoPortNum(((Integer)parser.getOptionValue(videoPortNumOption, getVideoPortNum())).intValue()); + // String cmd = args[1+argOffset]; + setCmd((String)parser.getOptionValue(commandOption,"fig8")); + if (getCmd().equalsIgnoreCase("followLine")){ + System.out.println("videoServer is ("+getVideoServer()+")"); + System.out.println("videoPortNum is ("+getVideoPortNum()+")"); + } + Integer velocityInt = (Integer)parser.getOptionValue(velocityOption,new Integer(0)); + Integer distanceInt = (Integer)parser.getOptionValue(distanceOption,new Integer(0)); + try { +// velocity = (int)(Integer.parseInt( args[2+argOffset])); +// distance = (int)(Integer.parseInt( args[3+argOffset] )); + setVelocity(velocityInt.intValue()); + System.out.println("velocity is ("+getVelocity()+")"); + setDistance(distanceInt.intValue()); + System.out.println("distance is ("+getDistance()+")"); + setWaittime(Math.abs((getDistance()/getVelocity()) * 900)); // waittime in ms (correction .9) + System.out.println("waittime is ("+getWaittime()+")"); + if (getWaittime() == 0) { + System.out.println("Invalid waittime "+getWaittime()); + } + if (getMinLineWidth() == 0) { + System.out.println("Invalid MinLineWidth "+getMinLineWidth()); + } else { + System.out.println("MinLineWidth is ("+getMinLineWidth()+")"); + } + if (getMaxLineWidth() == 0) { + System.out.println("Invalid MaxLineWidth "+getMaxLineWidth()); + } else { + System.out.println("MaxLineWidth is ("+getMaxLineWidth()+")"); + } + System.out.println("velocity: " + getVelocity() + " distance: " + getDistance() + " waittime: " + getWaittime()); + System.out.println("width: " + getWidth() + "height: " + getHeight() + "\n"); + } catch( Exception e ) { + System.err.println(e.getMessage()); + System.err.println("Couldn't parse velocity or distance2"); + System.exit(1); + } +// for( int i=4+argOffset; i < args.length; i++ ) { +// if( args[i].endsWith("debug") ) +// debug = true; +// } + Boolean debugBool = (Boolean)parser.getOptionValue(debugOption,new Boolean(false)); + setDebug(debugBool.booleanValue()); + System.out.println("debug is ("+isDebug()+")"); + Boolean hwHandShakeBool = (Boolean)parser.getOptionValue(hwHandShakeOption, new Boolean(false)); + setHwhandshake(hwHandShakeBool.booleanValue()); + setThresholdOverride((Integer)parser.getOptionValue(thresholdOption, new Integer(0))); + System.out.println("thresholdOverride is " + getThresholdOverride()); + System.out.println("hwHandShake is ("+isHwhandshake()+")"); + System.out.println("*** end of parseCmd"); + } + public int getRadius() { + return radius; + } + public void setRadius(int rad) { + radius = rad; + } + public int getDistance() { + return distance; + } + public void setDistance(int dist) { + distance = dist; + } + public boolean isHwhandshake() { + return hwhandshake; + } + public void setHwhandshake(boolean hwhandshake_) { + hwhandshake = hwhandshake_; + } + public int getAngle() { + return angle; + } + public void setAngle(int ang) { + angle = ang; + } + public boolean isDebug() { + return debug; + } + public void setDebug(boolean debug_) { + debug = debug_; + } + public String getPortname() { + return portname; + } + public void setPortname(String portname_) { + portname = portname_; + } + public String getCmd() { + return cmd; + } + public void setCmd(String cmd_) { + cmd = cmd_; + } + public int getVelocity() { + return velocity; + } + public void setVelocity(int velocity_) { + velocity = velocity_; + } + public int getWaittime() { + return waittime; + } + public void setWaittime(int waittime_) { + waittime = waittime_; + } + public String getProtocol() { + return protocol; + } + public void setProtocol(String protocol_) { + protocol = protocol_; + } + public class MyShutdown extends Thread { + RoombaComm roomba = null; + public MyShutdown(RoombaComm roomba){ + this.roomba = roomba; + } + public void run() { + System.out.println("MyShutdown hook called"); + if (roomba != null && roomba.isConnected()){ + System.out.println("roomba not null trying to stop and disconenct"); + roomba.stop(); + System.out.println("roomba stop issued"); + roomba.disconnect(); + System.out.println("roomba disconenct issued"); + }else{ + System.out.println("roomba was null or not connected"); + } + } + } + public int getThresholdOverride() { + return thresholdOverride; + } + public void setThresholdOverride(int thresholdOverride) { + this.thresholdOverride = thresholdOverride; + } + protected int getMaxLineWidth() { + return maxLineWidth; + } + protected void setMaxLineWidth(int maxLineWidth) { + this.maxLineWidth = maxLineWidth; + } + protected int getMinLineWidth() { + return minLineWidth; + } + protected void setMinLineWidth(int minLineWidth) { + this.minLineWidth = minLineWidth; + } + protected int getHeight() { + return height; + } + protected void setHeight(int height) { + this.height = height; + } + protected int getWidth() { + return width; + } + protected void setWidth(int width) { + this.width = width; + } + /** + * @return the videoServer + */ + protected String getVideoServer() { + return videoServer; + } + /** + * @param videoServer the videoServer to set + */ + protected void setVideoServer(String videoServer) { + this.videoServer = videoServer; + } + /** + * @return the videoPortNum + */ + protected int getVideoPortNum() { + return videoPortNum; + } + /** + * @param videoPortNum the videoPortNum to set + */ + protected void setVideoPortNum(int videoPortNum) { + this.videoPortNum = videoPortNum; + } + + } + + \ No newline at end of file diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RobotConnection.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RobotConnection.java new file mode 100644 index 0000000..504d19a --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RobotConnection.java @@ -0,0 +1,364 @@ +/* + * roombacomm.ArduinoClient -- test out the Arduino subsystem without robot motion + * + * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net + * + * 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 com.hackingroomba.roombacomm; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +public class RobotConnection implements Runnable { + private int waittime; + private String robotServer; + private int robotPortNum = 0; + private Socket socket; + public InputStream in; + public OutputStream out; + public enum ConnectionType {SERIAL, NET}; + private ConnectionType connectionType; + private int maxStringSize = 200; + byte[] readBytes = new byte[maxStringSize]; + byte[] readBytesBuf = new byte[maxStringSize]; + + // variables for the read thread + Thread thread; + private int readRequestLength = 0; + private int numBytesRead; + private boolean readComplete; + public enum ReadTerminator {LF, COUNT, NULL}; + private ReadTerminator readTerminator; + private int readTimeout = 1000; // 1000ms read timeout + + public RobotConnection(String robotServer, int preferredPortNum) { + connectionType = ConnectionType.NET; + robotPortNum = preferredPortNum; + this.robotServer = robotServer; + } + /** + * Initialize the connection with a server name and optional ':' delimited port number + * @param robotServer port name (if serial) or server IP. Server IP may have a trailing ":port" + */ + public RobotConnection(String robotServer) { + this.robotServer = robotServer; + } + + private void parseServerString(String robotServer) + { + // open a connection to robot (net or serial) + char portNameChar1 = robotServer.charAt(0); + if (portNameChar1 >= '0' && portNameChar1 <='9') { // portname begins with number, assume it's an IP + connectionType = ConnectionType.NET; + String s[] = robotServer.split(":"); + if (s.length == 2){ // if length = 1, leave robotServer alone - portNum was specified another way + this.robotServer = s[0]; + try { + robotPortNum = Integer.parseInt(s[1]); + } catch( Exception e ) { + robotPortNum = 0; + } + System.err.println("Using network server '" + this.robotServer + ":" + robotPortNum + "'" ); + } + + } else { + connectionType = ConnectionType.SERIAL; + this.robotServer = robotServer; // serial port name + System.out.println("Using serial port " + robotServer); + System.err.println("Error: Serial port not supported"); + } + } + + /** + * Create a network or serial connection and associated input & output streams. After creating + * the connection, the caller should get the input & output streams to use for reading & writing + * the connection. Alternatively, the caller can use the in and out objects. + */ + public boolean connect() + { + parseServerString(robotServer); // the server string can override the preferred port number & connectionType + if (connectionType == ConnectionType.NET) { + try { + if ((robotServer == null) || (robotPortNum == 0)) { + System.out.println("Error: server or port not set"); + return false; + } + // open a socket to the robot + socket = new Socket(robotServer, robotPortNum); + socket.setKeepAlive(true); + socket.setTcpNoDelay(true); + socket.setSoTimeout(0); + //socket.setSoTimeout(30000);// timeout in milliseconds - 30 sec + in = socket.getInputStream(); + out = socket.getOutputStream(); + + // hang a read thread on the socket + readComplete = true; // initialize the "go" variables + readRequestLength = 0; + thread = new Thread(this); + thread.setPriority(Thread.MAX_PRIORITY); + thread.start(); + } catch( Exception e ) { + System.out.println("connect: "+e); //e.printStackTrace(); + return false; + } + } else if (connectionType == ConnectionType.SERIAL) { + System.out.println("ERROR: SERIAL NOT IMPLEMENTED YET"); + return false; + } + return true; + } + + public void disconnect() { + try { + // do io streams need to be closed first? + if (in != null) in.close(); + if (out != null) out.close(); + } catch (Exception e) { + System.out.print("exception in disconnect"); + e.printStackTrace(); + } + in = null; + out = null; + + try { + if (socket != null) socket.close(); + } catch (Exception e) { + e.printStackTrace(); + } + socket = null; + } + public boolean send(int b) { // will also cover char + try { + //System.out.println("Send_( "+b+" & 0xff)"); + out.write(b & 0xff); // for good measure do the & + out.flush(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + public boolean send(byte[] bytes) { + try { + //logmsg("Send_byte( "+bytes+")"); + out.write(bytes); + out.flush(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + public boolean sendToArduino(int b) { // will also cover char + byte [] arduinoHeader = {'m', ' '}; // roomba commands to arduino are prefixed with m & end with LF + byte arduinoTrailer = '\r'; + + try { + //System.out.println("Send_( "+b+" & 0xff)"); + out.write(arduinoHeader); + out.write(b & 0xff); // for good measure do the & + out.write(arduinoTrailer); + out.flush(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + public boolean sendToArduino(byte[] bytes) { + byte [] arduinoHeader = {'m', ' '}; // roomba commands to arduino are prefixed with m & end with LF + byte arduinoTrailer = '\r'; + + try { + //logmsg("Send_byte( "+bytes+")"); + out.write(arduinoHeader); + out.write(bytes); + out.write(arduinoTrailer); + out.flush(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + public byte [] readBot(int count) throws Exception + { + byte [] returnData = new byte[count]; + readComplete = false; + readTerminator = ReadTerminator.COUNT; + readRequestLength = count; + + // wait max 1000ms for read to complete - most anything should be done by then + //System.out.println("Trying to read " + readRequestLength + " bytes"); + for (int i=0; i<200; i++) { + Thread.sleep(5); // wait 5ms for answer to come back + if (readComplete == true) + break; + } + if (readComplete) { + System.arraycopy(readBytes, 0, returnData, 0, numBytesRead); + return (returnData); + } else { + return (null); + } + } + + public void flushInput() + { + while(true) { + String junk; + System.out.print("Flushing... "); + try { + junk = readBotToTerminator(ReadTerminator.LF); + if (junk != null) + System.out.println("Flushed: " + junk); + else + return; + } catch (Exception e) { + System.err.println("Exception while flushing" + e.getMessage()); + return; + } + } + } + + /** + * Read bytes from ArduinoBot up to \n, \r, or other terminator + * @return String read from robot + */ + public String readBotToTerminator(ReadTerminator terminator) throws Exception + { + readComplete = false; + readTerminator = terminator; + readRequestLength = maxStringSize; + numBytesRead = 0; + + // wait readTimout ms for read to complete - most anything should be done by then + Thread.sleep(10); + for (int i=0; i 0) { + readLength = in.read(buffer); // read & discard data following the terminator (handles extra \n) + //System.out.println("readByBlock discarded " + readLength + " bytes"); + } + Thread.sleep(5); + continue; + } + bytesLeftToRead = readRequestLength; + readBytes[0] = 0; // initialize data to empty string + //System.out.println("readByBlock starting to read"); + // ACHTUNG - buffer gets overwritten at the beginning by multiple read buffers + for (int i=0; i<5; i++) { + readLength = in.read(buffer, 0, (readRequestLength - bufferIndex)); // read as much as there is, even if its more than was requested + if ((readLength == -1) || (in == null)) { + System.out.println("Error in run: in.read() returned -1 or null; application exiting in 30s: " + readLength); + Thread.sleep(30000); + System.exit(-1);; + } + bytesLeftToRead -= readLength; + for (int j=0; j 0) && (((readTerminator == ReadTerminator.COUNT) && (bufferIndex >= readRequestLength)) || + ((readTerminator == ReadTerminator.LF) && (readBytes[bufferIndex-1] == '\n') && (bufferIndex > 1)) || // guard against a single \n from previous read causing premature termination + ((readTerminator == ReadTerminator.NULL) && (readBytes[bufferIndex-1] == '\0')) || + (bufferIndex == maxStringSize))) + { + numBytesRead = bufferIndex; + readRequestLength = 0; // flag this request as complete (so ditch all following data until next request + readComplete = true; + bufferIndex = 0; + //System.out.println("readByBlock completed read with bufferIndex: " + numBytesRead + " readLength: " + readLength); + break; + } else { + Thread.sleep(10); + if (bufferIndex > 0) { + //System.out.println("readByBlock has " + bufferIndex + ", trying again"); + } else { + System.out.print("."); + } + } + } + } catch (Exception e) { + System.out.println("Exception: readByBlock thread exiting" + e.getMessage()); + //e.printStackTrace(); + return; // causes thread to exit + } + //System.out.println("readByBlock read " + readLength + " bytes"); + } + } + + public InputStream getIn() { + return in; + } + public OutputStream getOut() { + return out; + } + public void setReadTimeout(int readTimeout) { + this.readTimeout = readTimeout; + } + +} \ No newline at end of file diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RobotType.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RobotType.java new file mode 100644 index 0000000..17d5e0f --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RobotType.java @@ -0,0 +1,75 @@ +package com.hackingroomba.roombacomm; + +public class RobotType { + // encoder constants + private final double ROOMBA_COUNTS_PER_INCH = 57; + private final double MOBOT_COUNTS_PER_INCH = 250/80; // 205 counts in 80 inches + private final double TANKBOT_COUNTS_PER_INCH = 1800; + public double countsPerInch = ROOMBA_COUNTS_PER_INCH; + + // spin tolerance constants + private final int ROOMBA_TOLERANCE = 2; + private final int MOBOT_TOLERANCE = 7; + private final int DEFAULT_TOLERANCE = 10; + public int tolerance = DEFAULT_TOLERANCE; + private final int FAST_ROOMBA_SPINSPEED = 100; + private final int SLOW_ROOMBA_SPINSPEED = 20; + private final int FAST_MOBOT_SPINSPEED = 120; + private final int SLOW_MOBOT_SPINSPEED = 80; + public int fastSpinSpeed; + public int slowSpinSpeed; + + // PID constants + double ROOMBA_KP = 2.0; + double ROOMBA_KI = 1.0; + double ROOMBA_KD = 0.0; + double MOBOT_KP = 0.1; + double MOBOT_KI = 0.05; + double MOBOT_KD = 0.1; + double KP; + double KI; + double KD; + + + // robot types + public enum robotTypes {roomba, frankenRoomba, tankbot, mobot}; + robotTypes robotType; + + RobotType (robotTypes rt) + { + robotType = rt; + if ((rt == robotTypes.roomba) || (rt == robotTypes.frankenRoomba)) { + countsPerInch = ROOMBA_COUNTS_PER_INCH; + tolerance = ROOMBA_TOLERANCE; // tolerance of angle for spinToHeading + KP = ROOMBA_KP; // roomba PID constants + KI = ROOMBA_KI; + KD = ROOMBA_KD; + fastSpinSpeed = FAST_ROOMBA_SPINSPEED; + slowSpinSpeed = SLOW_ROOMBA_SPINSPEED; + } else if (rt == robotTypes.tankbot) { + countsPerInch = TANKBOT_COUNTS_PER_INCH; + tolerance = DEFAULT_TOLERANCE; // tolerance of angle for spinToHeading + KP = ROOMBA_KP; // tankbot PID constants + KI = ROOMBA_KI; + KD = ROOMBA_KD; + fastSpinSpeed = FAST_ROOMBA_SPINSPEED; + slowSpinSpeed = SLOW_ROOMBA_SPINSPEED; + } else if (rt == robotTypes.mobot) { + countsPerInch = MOBOT_COUNTS_PER_INCH; + tolerance = MOBOT_TOLERANCE; // tolerance of angle for spinToHeading + KP = MOBOT_KP; // Mo'bot PID constants + KI = MOBOT_KI; + KD = MOBOT_KD; + fastSpinSpeed = FAST_MOBOT_SPINSPEED; + slowSpinSpeed = SLOW_MOBOT_SPINSPEED; + } + } + public robotTypes getRobotType() { + return robotType; + } + + public void setRobotType(robotTypes robotType) { + this.robotType = robotType; + } +} + diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java new file mode 100644 index 0000000..733ae53 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaComm.java @@ -0,0 +1,1690 @@ +/* + * RoombaComm Interface + * + * 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 com.hackingroomba.roombacomm; + +import gnu.io.SerialPort; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Map; + +/** + * The abstract base for all Roomba communications. + * + *

Overview

+ * This class contains the communications layer-independent parts of + * how to communicate with a Roomba. It does assume a very serial port-like + * interaction. + * + * Standard lifecyle of this object (and its subclasses)
+ *   RoombaComm roomba = new RoombaCommSubClass();  // (e.g. RoombaCommSerial)
+ *   roomba.listports();               // if implemented
+ *   roomba.connect("someportid");
+ *   roomba.startup();
+ *   roomba.updateSensors();
+ *   while( ... ) {
+ *      roomba.sensors();
+ *      roomba.playNote( 53, 12 );
+ *      roomba.goForward( 400 );
+ *      roomba.spinRight( 45 );
+ *      if( roomba.bump() ) roomba.goBackward( 100 );
+ *   }    
+ *   roomba.disconnect();
+ * 
+ * + *

API levels

+ * Describe different API levels + * + *

Sensor Functions

+ * Describe sensor functions + * + *

Sublass behavior

+ * Describe subclassing strategries + * + * + * @author Tod E. Kurt + * SVN id value is $Id: RoombaComm.java 182 2010-11-02 03:49:10Z bouchier $ + */ +public abstract class RoombaComm +{ + /** version of the library */ + static public final String VERSION = "0.96.3"; + + /** + * contains a list of all the ports + * keys are port names (e.g. "/dev/usbserial1") + * values are Boolean in-use indicator + */ + protected static Map ports = null; + + /** turns on/off various debugging messages */ + public boolean debug = false; + + public boolean isDebug() { + return debug; + } + + public void setDebug(boolean debug) { + this.debug = debug; + } + + /** distance between wheels on the roomba, in millimeters */ + public static final int wheelbase = 258; + /** mm/deg is circumference distance divided by 360 degrees */ + public static final float + millimetersPerDegree = (float)(wheelbase * Math.PI / 360.0); + /** mm/rad is a circumference distance divied by two pi */ + public static final float + millimetersPerRadian = (float)(wheelbase/2); + + /** default speed for movement operations if speed isn't specified */ + public static final int defaultSpeed = 200; + + /** default update time in ms for auto sensors update */ + public static final int defaultSensorsUpdateTime = 200; + + /** current mode, if known */ + int mode; + + /** current speed for movement operations that don't take a speed */ + public int speed = defaultSpeed; + + /** computed boolean for when Roomba is errored out of safe mode */ + boolean safetyFault = false; + /** if sensor variables have been updated successfully */ + protected boolean sensorsValid = false; + /** Set to true to make sensors auto-update (at expense of serial b/w) */ + boolean sensorsAutoUpdate = false; + /** Time in milliseconds between sensor updates */ + int sensorsUpdateTime = 200; + /** last time (System.currentTimeMillis) that the sensors were updated */ + protected long sensorsLastUpdateTime; + /** how many bytes we expect to read from the sensor command */ + protected int readRequestLength; + + /** internal storage for all roomba sensor data */ + protected byte[] sensor_bytes = new byte[1024]; + + /** connected to a serial port or not, not necessarily to roomba */ + boolean connected = false; + + public boolean isConnected() { + String str = this.getSensorsAsString(); + if (str != null && str.length() >=1){ + if (debug){ + logmsg("isConnected found sensorString as ("+str+")"); + } + this.setConnected(true); + return true; + }else{ + if (debug){ + if (str != null){ + logmsg("isConnected found sensorString ("+str+")"); + }else{ + logmsg("isConnected found sensorString (null)"); + } + } + } + return connected; + } + + public void setConnected(boolean connected) { + this.connected = connected; + } + + /** set of flgs for the current state of the LEDs */ + /** note this is a superset of all protocol's supported */ + private boolean redOn = false; + private boolean greenOn = false; + private boolean toggleSpot = false; + private boolean toggleClean = false; + private boolean toggleMax = false; + private boolean toggleDirt = false; + private boolean toggleDock = false; + private boolean toggleCheckRobot = false; + private int power_color = 0; + private int power_int = 0; + + /** default RoombaComm protocol to identify classes of API calls to be made */ + private String protocol = "SCI"; + /** default baud rate for the default protocol */ + protected int rate = 57600; + + protected String portname = null; + /** connection object to use when appropriate */ + RobotConnection robotConnection; + + + /** + * Some "virtual" serial ports like Bluetooth serial on Windows + * return weird errors deep inside RXTX if an opened port is used + * before the virtual COM port is ready. One way to check that it + * is ready is to look for the DSR line going high. + * However, most simple, real serial ports do not do hardware handshaking + * so never set DSR high. + * Thus, if using Bluetooth serial on Windows, do: + * roombacomm.waitForDSR = true; + * before using it and see if it works. + */ + public boolean waitForDSR = false; + + /** The RXTX port object, normally you don't need access to this */ + public SerialPort serialPort = null; + + public RoombaComm() { + connected = false; + mode = MODE_UNKNOWN; + } + + public RoombaComm(boolean autoUpdate) { + this(); + if( autoUpdate ) + startAutoUpdate(); + } + + public RoombaComm(boolean autoUpdate, int updateTime) { + this(autoUpdate); + sensorsUpdateTime = updateTime; + } + + public RoombaComm(RobotConnection rc) { + robotConnection = rc; + } + public void startAutoUpdate() { + new Thread( new Runnable() { + public void run() { + try { + while( sensorsUpdateTime > 0 ) { + if( connected() ) sensors(); + Thread.sleep( sensorsUpdateTime ); + } + } catch(InterruptedException ex) {} + } + }).start(); + } + + /** + * List available ports + * @return a list available portids, if applicable + * or empty set if no ports, + * or return null if list is not enumerable + */ + public abstract String[] listPorts(); + + /** + * Connect to a port + * (for serial, portid is serial port name, for net, portid is url?) + * @return true on successful connect, false otherwise + */ + public abstract boolean connect(String portid); + /** + * Disconnect from a port, clean up any memory in use + */ + public abstract void disconnect(); + + + /** + * Send given byte array to Roomba. + * @param bytes byte array of ROI commands to send + * @return true on successful send + */ + public abstract boolean send(byte[] bytes); + + /** + * Send a single byte to the Roomba + * (defined as int because of stupid java signed bytes) + * @param b byte of an ROI command to send + * @return true on successful send + */ + public abstract boolean send(int b); + + /** + * Query Roomba for sensor status and sync its state with this object's + * Subclasses should query Roomba and fill up 'sensor_bytes' with the full + * sensor data set + * If a RooombaComm object is constructed with 'autoUpdate' true, + * calling this method is not required because a separate thread is created + * to do sensor updating. + * + * @return true on successful sensor update, false otherwise + */ + //public abstract boolean updateSensors(); + + /** + * Wake's Roomba up, if possible, thus optional + * To wake up the Roomba requires twiddling its DD line, often + * hooked up to the RS-232 DTR line, which may not be available in some + * implementations + */ + public void wakeup() { + logmsg("subclass has not implemented"); +// byte cmd[] = { (byte)POWER, (byte)v, (byte)power_color, (byte)power_intensity }; +// send(cmd); +// MSComm1.Output = "+++" & Chr(13) +// MSComm1.Output = "ATSW22,6,1,1" & Chr(13) +// MSComm1.Output = "ATSW23,6,0,1" & Chr(13) +// MSComm1.Output = "ATSW23,6,1,1" & Chr(13) +// MSComm1.Output = "ATMD" & Chr(13) + String str="+++\nATSW22,6,1,1\n,ATSW23,6,0,1\nATSW23,6,1,1\nATMD\n"; + send(str.getBytes()); +// byte bytes[] = str.getBytes(); +// for (int i = 0; i < bytes.length; i++) { +// +// } + } + + /** + * Put Roomba in safe mode. + * As opposed to full mode. Safe mode is the preferred working state + * when playing with the Roomba as it provides some measure of + * autonomous self-preservation if it encounters a cliff or is picked up + * If that happens it goes into passive mode and must be 'reset()'. + * @see #reset() + */ + public void startup() { + logmsg("startup"); + speed = defaultSpeed; + start(); + } + + /** + * Reset Roomba after a fault. This takes it out of whatever mode it was + * in and puts it into safe mode. + * This command also syncs the object's sensor state with the Roomba's + * by calling updateSensors() + * @see #startup() + * @see #updateSensors() + */ + public void reset() { + logmsg("reset"); + stop(); + startup(); + control(); + updateSensors(); + } + + /** Send START command */ + public void start() { + logmsg("start"); + mode = MODE_PASSIVE; + send( START ); + } + /** Send CONTROL command */ + public void control() { + logmsg("control"); + mode = MODE_SAFE; + send( CONTROL ); + // set blue dirt LED on so we know roomba is powered on & under control + // (and we don't forget to turn it off, and run it's batteries flat) + // FIXME: first time after a poweron, the lights flash then turn off + setLEDs(false, false, false, false, false, true, 128, 255); + } + /** Send SAFE command */ + public void safe() { + logmsg("safe"); + mode = MODE_SAFE; + send( SAFE ); + } + /** Send FULL command */ + public void full() { + logmsg("full"); + mode = MODE_FULL; + send( FULL ); + } + + /** + * Power off the Roomba. Once powered off, the only way to wake it + * is via wakeup() (if implemented) or via a physically pressing + * the Power button + * @see #wakeup() + */ + public void powerOff() { + logmsg("powerOff"); + mode = MODE_UNKNOWN; + send( POWER ); + } + + /** Send the SPOT command */ + public void spot() { + logmsg("spot"); + mode = MODE_PASSIVE; + send( SPOT ); + } + /** Send the CLEAN command */ + public void clean() { + logmsg("clean"); + mode = MODE_PASSIVE; + send( CLEAN ); + } + /** Send the max command */ + public void max() { + logmsg("max"); + mode = MODE_PASSIVE; + send( MAX ); + } + /** Send the max command */ + public void dock() { + logmsg("dock"); + mode = MODE_PASSIVE; +// send( CLEAN ); + send( DOCK ); + } + /** + * Send the SENSORS command + * with one of the SENSORS_ arguments + * Typically, one does "sensors(SENSORS_ALL)" to get all sensor data + * @param packetcode one of SENSORS_ALL, SENSORS_PHYSICAL, + * SENSORS_INTERNAL, or SENSORS_POWER, or for roomba 5xx, it + * is the sensor packet number (from the spec) + */ + public void sensors(int packetcode ) { + sensorsValid = false; + logmsg("sensors:"+packetcode); + switch (packetcode) { + case 0: readRequestLength = 26; break; + case 1: readRequestLength = 10; break; + case 2: readRequestLength = 6; break; + case 3: readRequestLength = 10; break; + case 4: readRequestLength = 14; break; + case 5: readRequestLength = 12; break; + case 6: readRequestLength = 52; break; + case 100: readRequestLength = 80; break; + case 101: readRequestLength = 28; break; + case 106: readRequestLength = 12; break; + case 107: readRequestLength = 9; break; + case 19: + case 20: + case 22: + case 23: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 39: + case 40: + case 41: + case 42: + case 43: + case 44: + case 46: + case 47: + case 48: + case 49: + case 50: + case 51: + case 54: + case 55: + case 56: + case 57: readRequestLength = 2; break; + default: readRequestLength = 1; break; + } + + byte cmd[] = { (byte)SENSORS, (byte)packetcode}; + send(cmd); + } + + /** + * get all sensor data + */ + public void sensors() { + readRequestLength = 26; + sensors( SENSORS_ALL ); + } + /** + * Read roomba 26-byte sensor record using robotConnection. Tries once to read valid data, allowing 100ms + * timeout on each attempt. + * @return true if read 26 bytes of valid data. Data has been stored in sensor_bytes. False otherwise + */ + public boolean updateSensors() + { + return updateSensors(SENSORS_ALL); + } + + public boolean updateSensors(int sensorGroup) + { + int sensorGroupSize; + + if (robotConnection == null) { + System.out.println("Error at ArduinoBot.updateSensors(): no connection object for robot"); + return false; + } + + switch(sensorGroup) { + case SENSORS_ALL: sensorGroupSize = 26; break; + case 100: sensorGroupSize = 80; break; + default: + System.err.println("Invalid sensor group in updateSensors(): " + sensorGroup); + return false; + } + sensors(sensorGroup); + return getSensorData(sensorGroupSize); + } + + /** + * Query a list of sensors. This is a roomba 5xx only command. + * @param sensorList A byte array containing the sensor groups requested to be read + * @param returnLen The number of bytes of data expected to be returned from roomba + */ + public void queryList(byte[] sensorList, int returnLen) + { + int i = 0; + int j; + + sensorsValid = false; + readRequestLength = returnLen; + byte cmd[] = new byte[2+sensorList.length]; + cmd[i++] = (byte) QUERYLIST; + cmd[i++] = (byte)sensorList.length; + for (j=0; j 1) || (readData[1] < 0)) && (sensorGroupSize == 26)) { + sensorsValid = false; + logmsg("updateSensors: received invalid data while attempting to read Roomba sensors!"); + } else { + sensorsValid = true; + System.arraycopy(readData, 0, sensor_bytes, 0, sensorGroupSize); + logmsg("updateSensors: sensorsValid!"); + //elapsedTime = System.currentTimeMillis() - startTime; + return true; + } + } + System.out.println("Error: timeout on sensor read"); + return false; + } + // + // basic functions + // + + /** + * Alias to pause + * @see #pause(int) + */ + public void delay( int millis ) { pause( millis ); } + + /** + * Just a simple pause function. + * Makes the thread block with Thread.sleep() + * @param millis number of milliseconds to wait + */ + public void pause( int millis ) { + try { Thread.sleep(millis); } catch(Exception e) { } + } + + + // + // higher-level functions + // + + /** + * Stop Rooomba's motion. + * Sends drive(0,0) + */ + public void stop() { + logmsg("stop"); + drive( 0, 0 ); + } + + /** Set speed for movement commands */ + public void setSpeed( int s ) { speed = Math.abs(s); } + /** Get speed for movement commands */ + public int getSpeed() { return speed; } + + /** + * Go straight at the current speed for a specified distance. + * Positive distance moves forward, negative distance moves backward. + * This method blocks until the action is finished. + * @param distance distance in millimeters, positive or negative + */ + public void goStraight( int distance ) { + float pausetime = Math.abs(distance / speed); // mm/(mm/sec) = sec + if (distance > 0) + goStraightAt( speed ); + else + goStraightAt( -speed); + pause( (int)(pausetime*1000) ); + stop(); + } + + /** + * @param distance distance in millimeters, positive + */ + public void goForward( int distance ) { + if( distance < 0 ) return; + goStraight( distance ); + } + + /** + * @param distance distance in millimeters, positive + */ + public void goBackward( int distance ) { + if( distance < 0 ) return; + goStraight( -distance ); + } + + /** + * + */ + public void turnLeft() { + turn(129); + } + public void turnRight() { + turn(-129); + } + public void turn( int radius ) { + drive( speed, radius ); + } + + /** + * Spin right or spin left a particular number of degrees + * @param angle angle in degrees, + * positive to spin left, negative to spin right + */ + public void spin( int angle ) { + if( angle > 0 ) spinLeft( angle ); + else if( angle < 0 ) spinRight( -angle ); + } + + /** + * Spin right the current speed for a specified angle + * @param angle angle in degrees, positive + */ + public void spinRight( int angle ) { + if( angle < 0 ) return; + float pausetime = Math.abs( millimetersPerDegree * angle / speed ); + spinRightAt( Math.abs(speed) ); + pause( (int)(pausetime*1000) ); + stop(); + } + + /** + * Spin left a specified angle at a specified speed + * @param angle angle in degrees, positive + */ + public void spinLeft( int angle ) { + if( angle<0 ) return; + //float pausetime = + float pausetime = Math.abs( millimetersPerDegree * angle / speed ); + spinLeftAt( Math.abs(speed) ); + pause( (int)(pausetime*1000) ); + stop(); + } + + /** + * Spin in place anti-clockwise, at the current speed + */ + public void spinLeft() { + spinLeftAt( speed ); + } + /** + * Spin in place clockwise, at the current speed + */ + public void spinRight() { + spinRightAt( speed ); + } + + /** + * Spin in place anti-clockwise, at the current speed. + * @param aspeed speed to spin at + */ + public void spinLeftAt(int aspeed) { + drive( aspeed, 1 ); + } + + /** + * Spin in place clockwise, at the current speed. + * @param aspeed speed to spin at, positive + */ + public void spinRightAt(int aspeed) { + drive( aspeed, -1 ); + } + + // + // mid-level movement, no blocking, parameterized by speed, not distance + // + + /** + * Go straight at a specified speed. + * Positive is forward, negative is backward + * @param velocity velocity of motion in mm/sec + */ + public void goStraightAt( int velocity ) { + //System.out.println("goStraightAt: velocity:"+velocity); + if( velocity > 500 ) velocity = 500; + if( velocity < -500 ) velocity = -500; + drive( velocity, 0x8000 ); + } + + /** + * Go forward the current (positive) speed + */ + public void goForward() { + goStraightAt( Math.abs(speed) ); + } + + /** + * Go backward at the current (negative) speed + */ + public void goBackward() { + goStraightAt( - Math.abs(speed) ); + } + + /** + * Go forward at a specified speed + */ + public void goForwardAt( int aspeed ) { + if( aspeed < 0 ) return; + goStraightAt( aspeed ); + } + + /** + * Go backward at a specified speed + */ + public void goBackwardAt( int aspeed ) { + if( aspeed < 0 ) return; + goStraightAt( -aspeed ); + } + + + // + // low-level movement and action + // + + /** + * Move the Roomba via the low-level velocity + radius method. + * See the 'Drive' section of the Roomba ROI spec for more details. + * Low-level command. + * @param velocity speed in millimeters/second, + * positive forward, negative backward + * @param radius radius of turn in millimeters + */ + public void drive( int velocity, int radius ) { + byte cmd[] = { (byte)DRIVE,(byte)(velocity>>>8),(byte)(velocity&0xff), + (byte)(radius >>> 8), (byte)(radius & 0xff) }; + logmsg("drive: "+hex(cmd[0])+","+hex(cmd[1])+","+hex(cmd[2])+","+ + hex(cmd[3])+","+hex(cmd[4])); + send( cmd ); + } + + /** + * Play a musical note + * Does it via the hacky method of defining a one-note song & playing it + * Uses up song slot 15. + * If another note is played before one is finished, the new note cuts off + * the old one. + * @param note a note number from 31 (G0) to 127 (G8) + * @param duration duration of note in 1/64ths of a second + */ + public void playNote( int note, int duration ) { + logmsg("playnote: "+note+":"+duration); + byte cmd[] = { + (byte)SONG, 3, 1, (byte)note, (byte)duration, // define song + (byte)PLAY, 3 }; // play it back + send( cmd ); + } + + public void playSong( int songnum ) { + byte cmd[] = { (byte)PLAY, (byte)songnum }; + send(cmd); + } + + /** + * Make a song + * @param songnum number of song to define + * @param song array of songnotes, + * even entries are notenums, odd are duration of 1/6ths + */ + public void createSong( int songnum, int song[] ) { + int len = song.length; + int songlen = len/2; + logmsg("createSong: songnum:"+songnum+", songlen:"+songlen); + byte cmd[] = new byte[len+3]; + cmd[0] = (byte) SONG; + cmd[1] = (byte) songnum; + cmd[2] = (byte) songlen; + for( int i=0; i < len; i++ ) { + cmd[3+i] = (byte)song[i]; + } + send(cmd); + } + /** + * Make a song + * @param songnum number of song to define + * @param song array of Notes + */ + public void createSong( int songnum, Note song[] ) { + int songlen = song.length; + logmsg("createSong: songnum:"+songnum+", songlen:"+songlen); + byte cmd[] = new byte[songlen+3]; + cmd[0] = (byte) SONG; + cmd[1] = (byte) songnum; + cmd[2] = (byte) songlen; + int j=3; + for( int i=0; i < songlen; i++ ) { + cmd[j++] = (byte)song[i].notenum; + cmd[j++] = (byte)song[i].toSec64ths(); + } + send(cmd); + } + + + + /** + * Turns on/off the non-drive motors (main brush, vacuum, sidebrush). + * Sort of low-level. + * @param mainbrush mainbrush motor on/off state + * @param vacuum vacuum motor on/off state + * @param sidebrush sidebrush motor on/off state + */ + public void setMotors(boolean mainbrush,boolean vacuum,boolean sidebrush) { + byte cmd[] = { + (byte)MOTORS, + (byte)((mainbrush?0x04:0) | (vacuum?0x02:0) | (sidebrush?0x01:0))}; + send( cmd ); + } + + /** + * Turns on/off the various LEDs. + * Low-level command. + * FIXME: this is too complex + */ + public void setLEDs( boolean status_green, boolean status_red, + boolean spot,boolean clean,boolean max,boolean dirt, + int power_color, int power_intensity ) { + int v = (status_green?0x20:0) | (status_red?0x10:0) | + (spot?0x08:0) | (clean?0x04:0) | (max?0x02:0) | (dirt?0x01:0); + logmsg("setLEDS: "+binary(v)); + byte cmd[] = { (byte)LEDS, (byte)v, + (byte)power_color, (byte)power_intensity }; + send(cmd); + } + + //500 series + public void setLEDsOI( boolean checkRobot, boolean spot,boolean dock,boolean dirt, + int power_color, int power_intensity ) { + updateDisplay("setLEDsOI ("+checkRobot+")("+spot+")("+dock+")("+dirt+")("+power_color+")("+power_intensity+")", this.debug); + int v = (checkRobot?0x08:0) | (dock?0x04:0) | (spot?0x02:0) | (dirt?0x01:0); + logmsg("setLEDS: "+binary(v)); + byte cmd[] = { (byte)LEDS, (byte)v, + (byte)power_color, (byte)power_intensity }; + // TODO: find a way to do an updateDisplay with a byte array + send(cmd); +} + + /** + * Turn all vacuum motors on or off according to state + * @param state true to turn on vacuum function, false to turn it off + */ + public void vacuum(boolean state) { + logmsg("vacuum: "+state); + setMotors(state,state,state); + } + + + // + // sensor functions + // + + + /** + * Compute possible safety fault. + * Called on every successful updateSensors(). + * In normal use, call updateSensors() then check safetyFault(). + * @return true if indicates we had an event that took the Roomba out of + * safe mode + * @see #updateSensors() + */ + public boolean computeSafetyFault() { + safetyFault = (sensor_bytes[BUMPSWHEELDROPS] & WHEELDROP_MASK) != 0 || + sensor_bytes[CLIFFLEFT]==1 || sensor_bytes[CLIFFFRONTLEFT]==1 || + sensor_bytes[CLIFFRIGHT]==1 || sensor_bytes[CLIFFFRONTRIGHT]==1; + + if( safetyFault && (mode == MODE_SAFE) ) mode = MODE_PASSIVE; + + return safetyFault; + } + + /** + * Returns current connected state. + * It's up to subclasses to ensure this variable is correct. + * @return current connected state + */ + public boolean connected() { return connected; } + + /** current ROI mode RoombaComm thinks the Roomba is in */ + public int mode() { return mode; } + /** mode as String */ + public String modeAsString() { + String s=null; + switch(mode) { + case MODE_UNKNOWN: s = "unknown"; break; + case MODE_PASSIVE: s = "passive"; break; + case MODE_SAFE: s = "safe"; break; + case MODE_FULL: s = "full"; break; + } + return s; + } + + /** */ + public boolean sensorsAutoUpdate() { return sensorsAutoUpdate; } + /** */ + public void setSensorsAutoUpdate(boolean b) { sensorsAutoUpdate=b; } + /** */ + public int sensorsUpdateTime() { return sensorsUpdateTime; } + /** */ + public void setSensorsUpdateTime(int i) { sensorsUpdateTime=i; } + + /** + * + */ + public boolean safetyFault() { return safetyFault; } + + /** + * + */ + public boolean sensorsValid() { + // FIXME: +// if( sensorsValid ) { // may be valid but stale +// long difftime = System.currentTimeMillis() - sensorsLastUpdateTime; +// if( difftime > 2*sensorsUpdateTime ) { // give it some space +// return false; +// } +// else return true; +// } + return sensorsValid; + } + public String getSensorsAsString() { + return sensorsAsString(); + } + public String convertByteArrayToString(byte[] byteArray) { + String value = new String(byteArray); + return value; + } + + + /** + * @return all sensor data as a string + */ + //* this likely needs to know about protocal to know how to read the sensors */ + public String sensorsAsString() { + String sd=""; + + if( debug ) { + sd = "\n"; + for( int i=0; i<26; i++ ) + sd += " "+hex(sensor_bytes[i]); + } + return + "*****\n" + + "bump:" + + (bumpLeft()?"l":"_") + + (bumpRight()?"r":"_") + + " wheel:" + + (wheelDropLeft() ?"l":"_") + + (wheelDropCenter()?"c":"_") + + (wheelDropRight() ?"r":"_") + + " wall:" + (wall() ?"Y":"n") + + " cliff:" + + (cliffLeft() ?"l":"_") + + (cliffFrontLeft() ?"L":"_") + + (cliffFrontRight() ?"R":"_") + + (cliffRight() ?"r":"_") + + " dirtL:"+dirtLeft()+ + " dirtR:"+dirtRight()+ "\n" + + "vwal:" + virtual_wall() + + " motr:" + motor_overcurrents() + + " dirt:" + dirt_left() + "," + dirt_right() + + " remo:" + hex(remote_opcode()) + + " butt:" + hex(buttons()) + + " dist:" + distance() + + " angl:" + angle() + "\n" + + "chst:" + charging_state() + + " volt:" + voltage() + + " curr:" + current() + + " temp:" + temperatureF() + "F" + + " chrg:" + charge() + + " capa:" + capacity() + + sd ; + } + public String chargeDataAsString() { + String sd=""; + if( debug ) { + sd = "\n"; + for( int i=0; i<26; i++ ) + sd += " "+hex(sensor_bytes[i]); + } + return + "Charging State: " + charging_state() + + " Temperature: " + temperatureF() + "F\n" + + "Voltage: " + voltage() + + " Current: " + current() + "\n" + + "Capacity: " + capacity() + + " Charge: " + charge() + + sd; + } + /** Did we bump into anything */ + public boolean bump() { + return (sensor_bytes[BUMPSWHEELDROPS] & BUMP_MASK) !=0; + } + /** Left bump sensor */ + public boolean bumpLeft() { + return (sensor_bytes[BUMPSWHEELDROPS] & BUMPLEFT_MASK) !=0; + } + /** Right bump sensor */ + public boolean bumpRight() { + return (sensor_bytes[BUMPSWHEELDROPS] & BUMPRIGHT_MASK) !=0; + } + /** Left wheeldrop sensor */ + public boolean wheelDropLeft() { + return (sensor_bytes[BUMPSWHEELDROPS] & WHEELDROPLEFT_MASK) !=0; + } + /** Right wheeldrop sensor */ + public boolean wheelDropRight() { + return (sensor_bytes[BUMPSWHEELDROPS] & WHEELDROPRIGHT_MASK) !=0; + } + /** Center wheeldrop sensor */ + public boolean wheelDropCenter() { + return (sensor_bytes[BUMPSWHEELDROPS] & WHEELDROPCENT_MASK) !=0; + } + /** Can we see a wall? */ + public boolean wall() { + return sensor_bytes[WALL] != 0; + } + + /** + * @return true if dirt present + */ + public boolean dirt() { + int dl = sensor_bytes[DIRTLEFT] & 0xff; + int dr = sensor_bytes[DIRTRIGHT] & 0xff; + //if(debug) println("Roomba:dirt: dl,dr="+dl+","+dr); + return (dl > 100) || (dr > 100); + } + /** + * amount of dirt seen by left dirt sensor + */ + public int dirtLeft() { + return dirt_left(); // yeah yeah + } + /** + * amount of dirt seen by right dirt sensor + */ + public int dirtRight() { + return dirt_right(); + } + + /** left cliff sensor */ + public boolean cliffLeft() { + return (sensor_bytes[CLIFFLEFT] != 0); + } + /** front left cliff sensor */ + public boolean cliffFrontLeft() { + return (sensor_bytes[CLIFFFRONTLEFT] != 0); + } + /** front right cliff sensor */ + public boolean cliffFrontRight() { + return (sensor_bytes[CLIFFFRONTRIGHT] != 0); + } + /** right cliff sensor */ + public boolean cliffRight() { + return sensor_bytes[CLIFFRIGHT] != 0; + } + + /** overcurrent on left drive wheel */ + public boolean motorOvercurrentDriveLeft() { + return (sensor_bytes[MOTOROVERCURRENTS] & MOVERDRIVELEFT_MASK) != 0; + } + /** overcurrent on right drive wheel */ + public boolean motorOvercurrentDriveRight() { + return (sensor_bytes[MOTOROVERCURRENTS] & MOVERDRIVERIGHT_MASK) != 0; + } + /** overcurrent on main brush */ + public boolean motorOvercurrentMainBrush() { + return (sensor_bytes[MOTOROVERCURRENTS] & MOVERMAINBRUSH_MASK) != 0; + } + /** overcurrent on vacuum */ + public boolean motorOvercurrentVacuum() { + return (sensor_bytes[MOTOROVERCURRENTS] & MOVERVACUUM_MASK) != 0; + } + /** overcurrent on side brush */ + public boolean motorOvercurrentSideBrush() { + return (sensor_bytes[MOTOROVERCURRENTS] & MOVERSIDEBRUSH_MASK) !=0; + } + + /** 'Power' button pressed state */ + public boolean powerButton() { + return (sensor_bytes[BUTTONS] & POWERBUTTON_MASK) != 0; + } + /** 'Spot' button pressed state */ + public boolean spotButton() { + return (sensor_bytes[BUTTONS] & SPOTBUTTON_MASK) != 0; + } + /** 'Clean' button pressed state */ + public boolean cleanButton() { + return (sensor_bytes[BUTTONS] & CLEANBUTTON_MASK) != 0; + } + /** 'Max' button pressed state */ + public boolean maxButton() { + return (sensor_bytes[BUTTONS] & MAXBUTTON_MASK) != 0; + } + + + // + // lower-level sensor access + // + /** lower-level func, returns raw byte */ + public int bumps_wheeldrops() { + return sensor_bytes[BUMPSWHEELDROPS]; + } + /** lower-level func, returns raw byte */ + public int cliff_left() { + return sensor_bytes[CLIFFLEFT]; + } + /** lower-level func, returns raw byte */ + public int cliff_frontleft() { + return sensor_bytes[CLIFFFRONTLEFT]; + } + /** lower-level func, returns raw byte */ + public int cliff_frontright() { + return sensor_bytes[CLIFFFRONTRIGHT]; + } + /** lower-level func, returns raw byte */ + public int cliff_right() { + return sensor_bytes[CLIFFRIGHT]; + } + /** lower-level func, returns raw byte */ + public int virtual_wall() { + return sensor_bytes[VIRTUALWALL]; + } + /** lower-level func, returns raw byte */ + public int motor_overcurrents() { + return sensor_bytes[MOTOROVERCURRENTS]; + } + /** */ + public int dirt_left() { + return sensor_bytes[DIRTLEFT] & 0xff; + } + /** */ + public int dirt_right() { + return sensor_bytes[DIRTRIGHT] & 0xff; + } + /** lower-level func, returns raw byte */ + public int remote_opcode() { + return sensor_bytes[REMOTEOPCODE]; + } + /** lower-level func, returns raw byte */ + public int buttons() { + return sensor_bytes[BUTTONS]; + } + + /** + * Distance traveled since last requested + * units: mm + * range: -32768 - 32767 + */ + public short distance() { + return toShort(sensor_bytes[DISTANCE_HI], + sensor_bytes[DISTANCE_LO]); + } + /** + * Angle traveled since last requested + * units: mm, diff in distance traveled by two drive wheels + * range: -32768 - 32767 + */ + public short angle() { + return toShort(sensor_bytes[ANGLE_HI], + sensor_bytes[ANGLE_LO]); + } + /** + * angle since last read, but in degrees + */ + // FIXME I think this should be (360 * angle())/(258 * PI) + public float angleInDegrees() { + return (float) angle() / millimetersPerDegree; + } + /** + * angle since last read, but in radians + */ + // FIXME I think this should be (2 * angle())/258 + public float angleInRadians() { + return (float) angle() / millimetersPerRadian; + } + + /** + * Charging state + * units: enumeration + * range: + */ + public int charging_state() { + return sensor_bytes[CHARGINGSTATE] & 0xff; + } + /** + * Voltage of battery + * units: mV + * range: 0 - 65535 + */ + public int voltage() { + return toUnsignedShort(sensor_bytes[VOLTAGE_HI], + sensor_bytes[VOLTAGE_LO]); + } + /** + * Current flowing in or out of battery + * units: mA + * range: -332768 - 32767 + */ + public short current() { + return toShort(sensor_bytes[CURRENT_HI], + sensor_bytes[CURRENT_LO]); + } + /** + * temperature of battery + * units: degrees Celcius + * range: -128 - 127 + */ + public byte temperature() { + return sensor_bytes[TEMPERATURE]; + } + public byte temperatureF() { + int c = sensor_bytes[TEMPERATURE]; + return (byte) ((9.0/5.0)*c + 32); + } + /** + * Current charge of battery + * units: mAh + * range: 0-65535 + */ + public int charge() { + return toUnsignedShort(sensor_bytes[CHARGE_HI], + sensor_bytes[CHARGE_LO]); + } + /** + * Estimated charge capacity of battery + * units: mAh + * range: 0-65535 + */ + public int capacity() { + return toUnsignedShort(sensor_bytes[CAPACITY_HI], + sensor_bytes[CAPACITY_LO]); + } + + // possible modes + public static final int MODE_UNKNOWN = 0; + public static final int MODE_PASSIVE = 1; + public static final int MODE_SAFE = 2; + public static final int MODE_FULL = 3; + + // Roomba ROI opcodes + // these should all be bytes, but Java bytes are signed, sucka + public static final int START = 128; // 0 + public static final int BAUD = 129; // 1 + public static final int CONTROL = 130; // 0 + public static final int SAFE = 131; // 0 + public static final int FULL = 132; // 0 + public static final int POWER = 133; // 0 + public static final int SPOT = 134; // 0 + public static final int CLEAN = 135; // 0 + public static final int MAX = 136; // 0 + public static final int DRIVE = 137; // 4 + public static final int MOTORS = 138; // 1 + public static final int LEDS = 139; // 3 + public static final int SONG = 140; // 2N+2 + public static final int PLAY = 141; // 1 + public static final int SENSORS = 142; // 1 + public static final int DOCK = 143; // 0 + public static final int PWMMOTORS = 144; // 3 + public static final int DRIVEWHEELS = 145; // 4 + public static final int DRIVEPWM = 146; // 4 + public static final int STREAM = 148; // N+1 + public static final int QUERYLIST = 149; // N+1 + public static final int STOPSTARTSTREAM = 150; // 1 + public static final int SCHEDULINGLEDS = 162; // 2 + public static final int DIGITLEDSRAW = 163; // 4 + public static final int DIGITLEDSASCII = 164; // 4 + public static final int BUTTONSCMD = 165; // 1 + public static final int SCHEDULE = 167; // n + public static final int SETDAYTIME = 168; // 3 + + // offsets into sensor_bytes data + public static final int BUMPSWHEELDROPS = 0; + public static final int WALL = 1; + public static final int CLIFFLEFT = 2; + public static final int CLIFFFRONTLEFT = 3; + public static final int CLIFFFRONTRIGHT = 4; + public static final int CLIFFRIGHT = 5; + public static final int VIRTUALWALL = 6; + public static final int MOTOROVERCURRENTS = 7; + public static final int DIRTLEFT = 8; + public static final int DIRTRIGHT = 9; + public static final int REMOTEOPCODE = 10; + public static final int BUTTONS = 11; + public static final int DISTANCE_HI = 12; + public static final int DISTANCE_LO = 13; + public static final int ANGLE_HI = 14; + public static final int ANGLE_LO = 15; + public static final int CHARGINGSTATE = 16; + public static final int VOLTAGE_HI = 17; + public static final int VOLTAGE_LO = 18; + public static final int CURRENT_HI = 19; + public static final int CURRENT_LO = 20; + public static final int TEMPERATURE = 21; + public static final int CHARGE_HI = 22; + public static final int CHARGE_LO = 23; + public static final int CAPACITY_HI = 24; + public static final int CAPACITY_LO = 25; + + // bitmasks for various thingems + public static final int WHEELDROP_MASK = 0x1C; + public static final int BUMP_MASK = 0x03; + public static final int BUMPRIGHT_MASK = 0x01; + public static final int BUMPLEFT_MASK = 0x02; + public static final int WHEELDROPRIGHT_MASK = 0x04; + public static final int WHEELDROPLEFT_MASK = 0x08; + public static final int WHEELDROPCENT_MASK = 0x10; + + public static final int MOVERDRIVELEFT_MASK = 0x10; + public static final int MOVERDRIVERIGHT_MASK= 0x08; + public static final int MOVERMAINBRUSH_MASK = 0x04; + public static final int MOVERVACUUM_MASK = 0x02; + public static final int MOVERSIDEBRUSH_MASK = 0x01; + + public static final int POWERBUTTON_MASK = 0x08; + public static final int SPOTBUTTON_MASK = 0x04; + public static final int CLEANBUTTON_MASK = 0x02; + public static final int MAXBUTTON_MASK = 0x01; + + // which sensor packet, argument for sensors(int) + public static final int SENSORS_ALL = 0; + public static final int SENSORS_PHYSICAL = 1; + public static final int SENSORS_INTERNAL = 2; + public static final int SENSORS_POWER = 3; + + public static final int REMOTE_NONE = 0xff; + public static final int REMOTE_POWER = 0x8a; + public static final int REMOTE_PAUSE = 0x89; + public static final int REMOTE_CLEAN = 0x88; + public static final int REMOTE_MAX = 0x85; + public static final int REMOTE_SPOT = 0x84; + public static final int REMOTE_SPINLEFT = 0x83; + public static final int REMOTE_FORWARD = 0x82; + public static final int REMOTE_SPINRIGHT = 0x81; + + /* +no button = -1 +power = -118 8a +pause = -119 89 +clean = -120 88 +max = -123 85 +spot = -124 84 +spinleft = -125 81 (8d keyup?) +forward = -126 82 (8c?) +spinright = -127 83 + */ + + // + // utility methods + // + + /** + * + */ + static public final short toShort(byte hi, byte lo) { + return (short)((hi << 8) | (lo & 0xff)); + } + /** + * + */ + static public final int toUnsignedShort(byte hi, byte lo) { + return (int)(hi & 0xff) << 8 | lo & 0xff; + } + + public void println(String s) { + System.out.println(s); + } + + public String hex(byte b) { + return Integer.toHexString(b&0xff); + } + + public String hex(int i) { + return Integer.toHexString(i); + } + + + public String binary(int i) { + return Integer.toBinaryString(i); + } + + /** + * just a little debug + */ + public void logmsg(String msg) { + if(debug) + { + System.err.println("RoombaComm ("+System.currentTimeMillis()+"):"+msg); + System.err.flush(); + } + } + + /** + * General error reporting, all corraled here just in case + * I think of something slightly more intelligent to do. + */ + public void errorMessage(String where, Throwable e) { + e.printStackTrace(); + throw new RuntimeException("Error inside Serial." + where + "()"); + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + if (protocol.equals("SCI")) { + rate = 57600; + } else if (protocol.equals("OI")) { + rate = 115200; + } + this.protocol = protocol; + logmsg("Protocol: " + protocol +" , rate: "+rate); + writeConfigFile(portname, protocol, waitForDSR?'Y':'N'); + } + + /** + * Write a config file with current settings + */ + protected void writeConfigFile(String port, String protocol, char waitForDSR) { + try { + FileWriter f = new FileWriter(".roomba_config", false); + BufferedWriter w = new BufferedWriter(f); // create file + if (port != null){ + w.write(port); + }else{ + w.newLine(); + } + w.newLine(); + if (protocol != null){ + w.write(protocol); + }else{ + w.newLine(); + } + w.newLine(); + w.write(waitForDSR); + w.newLine(); + w.close(); + f.close(); + } catch (IOException e) { + logmsg("unable to write .roomba_config " + e); + } + } + + protected void readConfigFile() { + try { + FileReader f = new FileReader(".roomba_config"); + BufferedReader r = new BufferedReader(f); + portname = r.readLine(); + setProtocol(r.readLine()); + if (getProtocol().equals("SCI")) { + rate = 57600; + }else if (getProtocol().equals("OI")) { + rate = 115200; + } + waitForDSR = r.readLine().equals("Y")?true:false; + logmsg("read config port: " + serialPort + " protocol: " + getProtocol() + " waitDSR: " + waitForDSR); + } catch (IOException e) { + logmsg("unable to read .roomba_config " + e); + } + } + + public void setLEDs(RoombaComm roombacomm) { + if( !roombacomm.connected() ){ + updateDisplay("not-connected", this.debug); + return; + } + updateDisplay("setLEDs protocal is ("+this.protocol+")", this.debug); + if (this.protocol.equalsIgnoreCase("SCI")){ + roombacomm.setLEDs(this.greenOn, this.redOn, this.toggleSpot, this.toggleClean, this.toggleMax, this.toggleDirt, + this.power_color, this.power_int); + } + if (this.protocol.equalsIgnoreCase("OI")){ + roombacomm.setLEDsOI(this.toggleCheckRobot, this.toggleSpot, this.toggleDock, this.toggleDirt, this.power_color, this.power_int); + updateDisplay("Checkrobot("+this.toggleCheckRobot +"),Spot("+ this.toggleSpot +"),Dock("+ this.toggleDock +"),Dirt("+ this.toggleDirt +"),Pcolor("+ this.power_color +"),Pint("+ this.power_int+")",true); + } + } + + public void setChgGreenLED(RoombaComm roombacomm, boolean green) { + this.greenOn=green; + updateDisplay("setChgGreenLED", true); + this.setLEDs(roombacomm); + } + + public void setChgRedLED(RoombaComm roombacomm, boolean red) { + this.redOn=red; + updateDisplay("setChgRedLED", true); + this.setLEDs(roombacomm); + } + + public void setChgSpotLED(RoombaComm roombacomm, boolean spot) { + this.toggleSpot=spot; + updateDisplay("setChgSpotLED value("+spot+")", true); + this.setLEDs(roombacomm); + } + + public void setChgCleanLED(RoombaComm roombacomm, boolean clean) { + updateDisplay("setChgCleanLED",true); + this.toggleClean=clean; + this.setLEDs(roombacomm); + } + + public void setChgMaxLED(RoombaComm roombacomm, boolean max) { + updateDisplay("setChgMaxLED",true); + this.toggleMax=max; + this.setLEDs(roombacomm); + } + + public void setChgDirtLED(RoombaComm roombacomm, boolean dirt) { + updateDisplay("setChgDirtLED",true); + this.toggleDirt=dirt; + this.setLEDs(roombacomm); + } + + public void setChgPowerColorLED(RoombaComm roombacomm, int power_color) { + updateDisplay("setChgPowerColorLED",true); + this.power_color=power_color; + this.setLEDs(roombacomm); + } + + public void setChgPowerIntensityLED(RoombaComm roombacomm, int power_intensity) { + updateDisplay("setChgPowerIntensityLED",true); + this.power_int=power_intensity; + this.setLEDs(roombacomm); + } + public void setChgCheckRobotLED(RoombaComm roombacomm, boolean toggleCheckRobot) { + this.toggleCheckRobot = toggleCheckRobot; + this.setLEDs(roombacomm); + } + + public void setChgDockLED(RoombaComm roombacomm, boolean toggleDock) { + this.toggleDock = toggleDock; + this.setLEDs(roombacomm); + } + protected void updateDisplay(String s, boolean onlyDebug) { + if (onlyDebug && debug){ + updateDisplay(s); + System.out.println(s); + } + } + + protected void updateDisplay(String s) { + // displayText.append( s ); + // displayText.setCaretPosition(displayText.getDocument().getLength()); + } + + public boolean isRedOn() { + return redOn; + } + + public boolean isGreenOn() { + return greenOn; + } + + public boolean isToggleSpot() { + return toggleSpot; + } + + public boolean isToggleClean() { + return toggleClean; + } + + public boolean isToggleMax() { + return toggleMax; + } + + public boolean isToggleDirt() { + return toggleDirt; + } + + public boolean isToggleCheckRobot() { + return toggleCheckRobot; + } + + + public boolean isToggleDock() { + return toggleDock; + } + + + /** + * Returns the number of bytes that have been read from serial + * and are waiting to be dealt with by the user. + * (from processing.serial.Serial) + * + private int available() { + return (bufferLast - bufferIndex); + } + + /** + * Return a byte array of anything that's in the serial buffer. + * Not particularly memory/speed efficient, because it creates + * a byte array on each read, but it's easier to use than + * readBytes(byte b[]) (see below). + * (from processing.serial.Serial) + * + private byte[] readBytes() { + if (bufferIndex == bufferLast) return null; + + synchronized (buffer) { + int length = bufferLast - bufferIndex; + byte outgoing[] = new byte[length]; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex = 0; // rewind + bufferLast = 0; + return outgoing; + } + } + + /** + * Grab whatever is in the serial buffer, and stuff it into a + * byte buffer passed in by the user. This is more memory/time + * efficient than readBytes() returning a byte[] array. + * + * Returns an int for how many bytes were read. If more bytes + * are available than can fit into the byte array, only those + * that will fit are read. + * (from processing.serial.Serial) + * + public int readBytes(byte outgoing[]) { + if (bufferIndex == bufferLast) return 0; + + synchronized (buffer) { + int length = bufferLast - bufferIndex; + if (length > outgoing.length) length = outgoing.length; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex += length; + if (bufferIndex == bufferLast) { + bufferIndex = 0; // rewind + bufferLast = 0; + } + return length; + } + } + */ + public void powerOn() { + logmsg("powerOn"); + mode = MODE_PASSIVE; + // MSComm1.Output = "+++" & Chr(13) + // MSComm1.Output = "ATSW22,6,1,1" & Chr(13) + // MSComm1.Output = "ATSW23,6,0,1" & Chr(13) + // MSComm1.Output = "ATSW23,6,1,1" & Chr(13) + // MSComm1.Output = "ATMD" & Chr(13 + send( ("+++"+(char)13).getBytes()); + send( ("ATSW22,6,1,1"+(char)13).getBytes()); + send( ("ATSW23,6,0,1"+(char)13).getBytes()); + send( ("ATSW23,6,1,1"+(char)13).getBytes()); + send( ("ATMD"+(char)13).getBytes()); + //: TYPE : ATSW22,6,0,1 ; First change it to high + //REPLY: OK + //TYPE : ATSW22,6,0,0 ; Change it to low + //REPLY: OK + //TYPE : ATSW22,6,0,1 + //REPLY: OK ; Change it to high + // send( "+++\n".getBytes()); + // send( "ATSW22,6,0,1\n".getBytes()); + // send( "ATSW22,6,0,0\n".getBytes()); + // send( "ATSW22,6,0,1\n".getBytes()); + // send( "ATMD\n".getBytes()); + + } + + public byte[] getSensor_bytes() { + return sensor_bytes; + } +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommCLI.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommCLI.java new file mode 100644 index 0000000..8e121ac --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommCLI.java @@ -0,0 +1,1571 @@ +/* + * roombacomm.RoombaCommCLI -- test out RoombaComm library by issuing commands to it from the command line + * + * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net + * + * 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 com.hackingroomba.roombacomm; + +import java.io.*; +import com.hackingroomba.roombacomm.RobotConnection.ReadTerminator; + +public class RoombaCommCLI { + // robot communications variables + private String curLine = ""; // Line read from standard in + private String [] args; + private String protocol; + private String portName; + private RobotConnection robotConnection = null; + private RobotConnection arduinoConnection = null; + private RobotConnection localizerConnection = null; + private RoombaComm robot; // use ArduinoBot as the robot, because it's the highest class + private ArduinoBot arduino; + private RobotType robotType; + + // encoder variables + int speed = 50; + int angle; + int distance = 0; + int lEncoder = 0; + int rEncoder = 0; + int lastLEncoder = 0; + int lastREncoder = 0; + int lEncoderOverflow = 0; + int rEncoderOverflow = 0; + + // navigation variables + private double x = 0; + private double y = 0; + private double targetX, targetY; + private boolean targetInitialized = false; + private int localizerCompassOffset = 0; // angle between localizer Y axis and compass North, add to vectorToStart angle to get compass to target + private boolean localizerOffsetInitialized = false; + private double distanceToTarget; + private int courseToTarget; + + //RoboRealm variables + private watchVideo wv; + private FrameProcessor fp; + private int rrShapeX; // X location in image frame of detected shape from RoboRealm + private int rrConfidence; + private int rrSize; + private int rrRelativeHeading; + private boolean roborealmConnected = false; + + // I/O variables + InputStreamReader isr = new InputStreamReader(System.in); + BufferedReader in = new BufferedReader(isr); + + private long startTime; + private long elapsedTime; + + public RoombaCommCLI() + { + // do nothing + } + // main - it all starts here + public static void main(String[] args) { + System.out.println("Enter a command (type 'help' for command listing or 'quit' to exit): "); + RoombaCommCLI cli = new RoombaCommCLI(); + cli.runCommands(); + } + + private void usage() { + System.out.print( + "Available Commands\n------------------\n" + + "help\n" + + "quit\n" + + "connect {ip:portnum | serialPort} protocol; protocol is OI or SCI or AR (tankbot) or FR (FrankenRoomba) or MO (Mo'bot)\n" + + "\tUse AR or MO for Arduino connection - skips roombaInit and does arduinoInit\n" + + "disconnect Disconnect robot\n" + + "safe Enter safe mode\n" + + "full Enter full mode\n" + + "speed {defaultSpeed} Set the default speed\n" + + "spin {angleToSpin} Spins based on timer - YMMV\n" + + "spinTo (compass heading}\n" + + "drive {distance in inches} Drives based on timer - YMMV - literally\n" + + "driveFor {distance in inches} [compass heading] Drives based on wheel encoders and compass\n" + + "driveToTarget Drive from current position to target\n" + + "rectangle {length, width} [heading] Drive a rectangle, length is 1st, 3rd leg, width is 2nd, 4th leg, heading is initial direction\n" + + "squaredance {length, heading} Drive square with side 'length', initial heading 'heading', use localizer to seek to target which should be set before run with setTarget" + + "squaredance2 {length, heading} Drive square with side 'length', initial heading 'heading'" + + "stop - Stop robot\n" + + "compass Print the current compass reading\n" + + "encoders Print the current wheel encoders reading\n" + + "currentHeading {heading} Set the offset between compass-reported heading and actual heading\n" + + "sensors\n" + + "localizer {IP:Port} Connect to localizer\n" + + "disconnectLocalizer\n" + + "localize - get and print localizer fix\n" + + "setTarget {x y} - save away the target location ((x,y) coordinate in feet) in the target variables\n" + + "vectorToTarget - compute distance, direction to target\n" + + "printTarget - Print the current target" + + "vectorToTarget - compute the angle (in localizer coordinate system) and distance to start point\n" + + "localizerXBearing {bearing of +X axis} - bearing represents compass bearing of +X axis , i.e. +ve clockwise from north to X axis\n" + + "roborealm - connect to RoboRealm\n" + + "rrsnap - snap a frame, send it to RoboRealm, and get SHAPES data from RoboRealm\n" + + "pointToVisualTarget - rotate to find and point at a visual target\n" + + "seekToVisualTarget - rotate then travel incrementally to a visual target\n" + + "test Run tests\n" + + "Connect notes: Frankenroomba: connect 192.168.11.2 FR; should have sdr.sh and sdar1.sh running on chumby\n" + + "Mobot: with USB serial cable to Serial3, and arduino on USB1, run sdar1.sh on chumby and connect 192.168.11.2:5002 MO" + ); + } + + private void runCommands() + { + while (true){ + // get the command line + try { + curLine = in.readLine(); + } catch (IOException e) { + // Print out the exception that occurred + System.out.println("Error reading line: " + e.getMessage()); + System.exit(0); + } + + // split the command line + args = curLine.split("\\s+"); +/* System.out.println("You typed: " + curLine); + System.out.println("Args found: " + args.length); + for (String s: args) { + System.out.println(s); + } +*/ + // parse the command & execute it + if (args[0].equalsIgnoreCase("quit")) { + robotConnection.disconnect(); + if (arduinoConnection != null) + arduinoConnection.disconnect(); + System.exit(0); + } else if (args[0].equalsIgnoreCase("help")) { + usage(); +/* + * Connect Commands + */ + } else if (args[0].equalsIgnoreCase("connect")) { + connect(); + } else if (args[0].equalsIgnoreCase("disconnect")) { + robotConnection.disconnect(); + robot = null; + if (arduinoConnection != null) + arduinoConnection.disconnect(); + arduino = null; +/* + * Roborama commands + */ + } else if (args[0].equalsIgnoreCase("squareDance")) { + squareDance(); + } else if (args[0].equalsIgnoreCase("squareDance2")) { + squareDance2(); + } else if (args[0].equalsIgnoreCase("squareDance3")) { + squareDance3(); + } else if (args[0].equalsIgnoreCase("tabletrip")) { + tabletrip(); + } else if (args[0].equalsIgnoreCase("robocolumbus")) { + robocolumbus(); +/* + * Mode Commands + */ + } else if (args[0].equalsIgnoreCase("safe")) { + robot.safe(); + } else if (args[0].equalsIgnoreCase("full")) { + robot.full(); + } else if (args[0].equalsIgnoreCase("speed")) { + if (args.length != 2) + System.out.println("Error: must specify speed"); + else { + speed = Integer.parseInt(args[1]); + } +/* + * Spin Commands + */ + } else if (args[0].equalsIgnoreCase("spin")) { + if (args.length != 2) + System.out.println("Error: must specify angle to spin"); + else { + // get heading +// if (protocol.equalsIgnoreCase("FR")) { +// System.out.print("Heading before turn: "); +// arduino.printCompass(); +// } + angle = Integer.parseInt(args[1]); + int rv = spinByCompass(angle); + if (rv < 0) + System.out.println("Error spinning"); + // get heading + if (protocol.equalsIgnoreCase("FR")) { + System.out.printf("\nHeading after turn: "); + arduino.printCompass(); + } + + } + } else if (args[0].equalsIgnoreCase("spinto")) { + if (args.length != 2) + System.out.println("Error: must specify angle to spin"); + else { + // get heading + if (protocol.equalsIgnoreCase("FR")) { + System.out.print("Heading before turn: "); + arduino.printCompass(); + } + angle = Integer.parseInt(args[1]); + spinToHeading(angle); + // getheading + if (protocol.equalsIgnoreCase("FR")) { + System.out.print(" Heading after turn: "); + arduino.printCompass(); + } + + } + } else if (args[0].equalsIgnoreCase("randomSpin")) { + while (true) { + angle = (int)(Math.random() * 358.0); + System.out.println("spinning to " + angle); + spinToHeading(angle); + try { + if (System.in.available() != 0){ + robot.stop(); + break; + } + } catch (Exception e) { + System.out.println("Exception: in System.in.available"); + System.exit(-1); + } + } +/* + * Drive Commands + */ + } else if (args[0].equalsIgnoreCase("stop")) { + System.out.println("Stopping robot"); + robot.stop(); + } else if (args[0].equalsIgnoreCase("drive")) { + if (args.length == 2) { + distance = Integer.parseInt(args[1]); + robot.setSpeed(speed); + robot.goStraight(distance * 25); // 25mm per inch, goStraight takes mm + } else if (args.length == 3) { + distance = Integer.parseInt(args[1]); + angle = Integer.parseInt(args[2]); + float pausetime = Math.abs((distance * 25) / speed); // mm/(mm/sec) = sec + System.out.println("driving speed " + speed + " angle " + angle + " pausetime " + pausetime); + robot.drive( speed, angle ); + robot.pause( (int)(pausetime*1000) ); + robot.stop(); + } + } else if (args[0].equalsIgnoreCase("drivefor")) { + if (args.length > 1) { + distance = Integer.parseInt(args[1]); + if (args.length > 2) + angle = Integer.parseInt(args[2]); + else + angle = -1; // drive in current direction + try { + driveByCompass(distance, angle); + } catch (Exception e) { + robot.stop(); + System.out.println("Error: driveFor took exception, robot stopped"); + e.printStackTrace(); + } + } + // drive in a rectangle, by compass + } else if (args[0].equalsIgnoreCase("rectangle")) { + rectangle(); + } else if (args[0].equalsIgnoreCase("driveToTarget")) { + seekToTarget(); +/* + * Sensor Commands + */ + } else if (args[0].equalsIgnoreCase("compass")) { + arduino.printCompass(); + } else if (args[0].equalsIgnoreCase("currentHeading")) { + if (args.length != 2) { + System.out.println("Error - must specify current heading"); + continue; + } + int currentMagHeading = Integer.parseInt(args[1]); + arduino.setCompassOffset(currentMagHeading); + } else if (args[0].equalsIgnoreCase("sensors")) { + if (!robot.updateSensors()) + System.err.println("Error attempting to read valid data from robot.UpdateSensors()"); + else + System.out.println(robot.getSensorsAsString()); + } else if (args[0].equalsIgnoreCase("initEncoders")) { + try { + readEncoderDistance(true); + } catch (Exception e) { + System.err.println("Exception in initEncoders" + e.getMessage()); + } + } else if (args[0].equalsIgnoreCase("encoders")) { + double readEncDistance = 0; + + startTime = System.currentTimeMillis(); + try { + readEncDistance = readEncoderDistance(false); + } catch (Exception e) { + System.err.println("Exception reading robot encoders: " + e.getMessage()); + } + long elapsedTime = System.currentTimeMillis() - startTime; + System.out.println("Distance: " + readEncDistance + " in " + elapsedTime + " ms"); + +/* + * Localizer commands + */ + } else if (args[0].equalsIgnoreCase("localizer")) { + connectLocalizer(); + } else if (args[0].equalsIgnoreCase("disconnectLocalizer")) { + localizerConnection.disconnect(); + } else if (args[0].equalsIgnoreCase("localize")) { + if (!localize()) { + System.out.println("Error reading localizer"); + continue; + } + } else if (args[0].equalsIgnoreCase("setTarget")) { + if (args.length < 3) { + System.out.println("Must provide x & y location of target (in feet)"); + continue; + } + saveTarget(args[1], args[2]); + } else if (args[0].equalsIgnoreCase("vectorToTarget")) { + if (!localize()) { + System.out.println("Error reading localizer"); + continue; + } + try { + vectorToTarget(); + } catch (Exception e) { + System.err.println("Exception computing vector to target - is target initialized?"); + continue; + } + } else if (args[0].equalsIgnoreCase("printTarget")) { + System.out.println("targetX: " + targetX + " targetY: " + targetY); + } else if (args[0].equalsIgnoreCase("localizerXBearing")) { + if (args.length != 2) { + System.out.println("Error - must specify bearing of +X axis"); + continue; + } + try { + // Subtract 90 from localizer X axis (measured by hand-compass) + localizerCompassOffset = (Integer.parseInt(args[1]) + 270) % 360; + arduino.writeConfigInt("localizerCompassOffset", localizerCompassOffset); + System.out.println("Wrote localizerCompassOffset to " + localizerCompassOffset); + localizerOffsetInitialized = true; + } catch (Exception e) { + System.err.println("Error saving localizerCompassOffset\n" + e.getMessage()); + } +/* + * RoboRealm commands + */ + } else if (args[0].equalsIgnoreCase("roborealm")) { + connectRoborealm(); + } else if (args[0].equalsIgnoreCase("rrsnap")) { + int rv; + try { + rv = rrsnap(); + } catch (Exception e) { + System.out.println(e.getMessage()); + continue; + } + System.out.print("Found shape confidence " + rrConfidence + " at X:" + rrShapeX + " (" + + rrRelativeHeading + " degrees), size " + rrSize); + } else if (args[0].equalsIgnoreCase("seekToVisualTarget")) { + seekToVisualTarget(0); + } else if (args[0].equalsIgnoreCase("pointToVisualTarget")) { + pointToVisualTarget(); +/* + * Uncategorized commands + */ + } else if (args[0].equalsIgnoreCase("test")) { + testHeadingChange(); + } else { + System.out.println("Invalid command"); + } + } + + } + /** + * Connect to robot using args + */ + private void connect() { + if (args.length > 1) { + portName = args[1]; + } + if (args.length > 2) { + protocol = args[2]; + } + if ((protocol == null) || (portName == null)) { + System.out.println("port name and protocol must be specified\n"); + return; + } + + try { + if (!connect(portName)) { + System.out.println("Error connecting to service"); + return; + } + if (protocol.equalsIgnoreCase("OI") || protocol.equalsIgnoreCase("SCI") || + protocol.equalsIgnoreCase("FR")) { + robotType = new RobotType(RobotType.robotTypes.roomba); + initRoomba(); + } else if (protocol.equalsIgnoreCase("FR")) { // FrankenRoomba + robotType = new RobotType(RobotType.robotTypes.frankenRoomba); + arduino.initArduinoBot(robotType); + } else if (protocol.equalsIgnoreCase("AR")) { // Arduino + robotType = new RobotType(RobotType.robotTypes.tankbot); + arduino.initArduinoBot(robotType); + } else if (protocol.equalsIgnoreCase("MO")) { + robotType = new RobotType(RobotType.robotTypes.mobot); + arduino.initArduinoBot(robotType); + } else { + System.out.println("Invalid protocol"); + return; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + /** + * Connect to a TCP or serial service, which can have roomba + * behind it. + * @param portName A string containing a serial port name (e.g. /dev/ttyUSB0, or COM12:) or an IP address + * with optional port number after it (e.g. 192.168.1.33:5001). + */ + public boolean connect(String portName) + { + int preferredPortNum = 5001; + + robotConnection = new RobotConnection(portName, preferredPortNum); + if (!robotConnection.connect()) { + System.out.println("Couldn't connect to " + portName); + return false; + } + // init robot, which is used for the standard roombacomm commands + if ((protocol.equalsIgnoreCase("AR")) || (protocol.equalsIgnoreCase("MO"))) { + robot = new ArduinoBot(robotConnection); // superclass of roombacomm + } else { + robot = new RoombaCommTCPClient(robotConnection); // superclass of roombacomm + } + robot.setConnected(true); + // if FrankenRoomba, set up a 2nd robot and connection + if (protocol.equalsIgnoreCase("FR")) { + arduinoConnection = new RobotConnection(portName, 5002); + if (!arduinoConnection.connect()) { + System.out.println("Couldn't connect to port 5002 on " + portName); + return false; + } + arduino = new ArduinoBot(arduinoConnection); // it's actually a superclass of roombacomm + arduino.setConnected(true); + System.out.println("Connected to arduino socket"); + } else { + // this is an uggly hack to cover up that lower level methods use 2 robots. + // it should be done by creating a new robot type: Frankenroomba, which sends the right commands to + // the right place + arduino = (ArduinoBot)robot; + } + return true; + } + + /** + * Initialize a Roomba + */ + public boolean initRoomba() + { + System.out.println("Roomba startup"); + robot.startup(); + robot.control(); + robot.pause(1000); + + System.out.println("Checking for Roomba... \n"); + // roombaCommTCPClient.setDebug(true); + //robot.updateSensors(); // do it once, which doesn't seem to return anything + //robot.pause(1000); + if (robot.updateSensors()) { + System.out.println("Roomba found!\n"); + System.out.println(robot.getSensorsAsString()); + } else { + System.out.println("No Roomba. :( Is it turned on?\n"); + return false; + } + // roombacomm.updateSensors(); + System.out.println("connected (" + robot.connected() + ")\n"); + if (robot.connected()) { + System.out.println("Playing some notes\n"); + robot.playNote(72, 10); // C + robot.pause(200); + robot.playNote(79, 10); // G + robot.pause(200); + robot.playNote(76, 10); // E + robot.pause(200); + } + robot.setSpeed(speed); + + return true; + } + /** + * Connect to robot using args + */ + private boolean connectLocalizer() { + String localizerPort = null; + + if (args.length > 1) { + localizerPort = args[1]; + } + localizerConnection = new RobotConnection(localizerPort, 5010); + if (!localizerConnection.connect()) { + System.out.println("Couldn't connect to localizer at: " + portName); + return false; + } + localizerConnection.setReadTimeout(30000); + System.out.println("Connected to localizer"); + return true; + } + + /* + * Spin an angle + * @param angle Angle to spin, positive is clockwise (like a compass) + */ + public int spinByCompass(int angle) + { + int rv; + int targetHeading; + + targetHeading = arduino.getCompass(); + if (targetHeading < 0) + return targetHeading; + targetHeading += angle; + targetHeading %= 360; + rv = spinToHeading(targetHeading); + return rv; + } + + /* + * Spin to a heading using the simple algorithm for roomba, tankbot, and PID for Mobot. Mo'bot has + * lots of inertia, so needs derivative to prevent overshoot, and operates in high-load environment + * like grass, so needs integral to get it moving. + * @param heading Desired direction + * @return 0 = success + */ + public int spinToHeading(int heading) + { + if (robotType.robotType == RobotType.robotTypes.mobot) { + return(spinToHeadingPID(heading)); + } else { + return (spinToHeadingSimple(heading)); + } + } + /* + * Spin to a heading + * @param target Compass direction to turn to + */ + public int spinToHeadingSimple(int target) + { + int currentHeading; + int angleToSpin, absAngleToSpin; + int spinSpeed; + + spinSpeed = robotType.fastSpinSpeed; + + if ((target > 359) || (target < 0)) { + System.out.println("Invalid heading: " + target); + return -1; + } + System.out.print("Heading: "); + while (true) { + try{ + currentHeading = arduino.getCompass(); + System.out.print(currentHeading + " "); + angleToSpin = headingChange(target, currentHeading); + absAngleToSpin = Math.abs(angleToSpin); + + if (currentHeading < 0) { + System.out.println("Read invalid heading: " + currentHeading); + robot.stop(); + return -2; + } + + // spin slow when we get close + if (absAngleToSpin < 10) { + spinSpeed = robotType.slowSpinSpeed; + } else { + spinSpeed = robotType.fastSpinSpeed; + } + + if ((absAngleToSpin < robotType.tolerance) || (System.in.available() > 0)) { + robot.stop(); + System.out.println(); + robot.pause(500); // pause to let motion stop, then recheck heading + currentHeading = arduino.getCompass(); + angleToSpin = headingChange(target, currentHeading); + absAngleToSpin = Math.abs(angleToSpin); + if (absAngleToSpin < robotType.tolerance) { + return 0; + } + } + if (angleToSpin > 0) + robot.spinRightAt(spinSpeed); + else + robot.spinLeftAt(spinSpeed); + + if (System.in.available() != 0){ + robot.stop(); + return -4; + } + Thread.sleep(100); // spin for 100ms + } catch (Exception e) { + e.printStackTrace(); + return -3; + } + } + } + + /* + * Spin to a heading with PID control + * @param heading Desired direction at end of spin + * @return 0 = success, negative for error + */ + public int spinToHeadingPID(int target) + { + int currentHeading; + int headingError, absHeadingError; + int spinSpeed; + double radiusPidOutput = 0; + + if ((target > 359) || (target < 0)) { + System.out.println("Invalid heading: " + target); + return -1; + } + + System.out.println("Spinning to heading " + target + " using PID"); + // initialize the direction control system + Pid radiusPid = new Pid(robotType.KP, robotType.KI, robotType.KD); + + // spin checking compass every 100ms + while (true) { + try{ + startTime = System.currentTimeMillis(); + + currentHeading = arduino.getCompass(); + System.out.print(currentHeading + " "); + headingError = headingChange(target, currentHeading); + absHeadingError = Math.abs(headingError); + + if (currentHeading < 0) { + System.out.println("Read invalid heading: " + currentHeading); + robot.stop(); + return -2; + } + + // stop and re-check if angle to spin < tolerance or user interrupt + if ((absHeadingError < robotType.tolerance) || (System.in.available() > 0)) { + robot.stop(); + robot.pause(500); // pause to let motion stop, then recheck heading + currentHeading = arduino.getCompass(); + System.out.println(currentHeading); + headingError = headingChange(target, currentHeading); + absHeadingError = Math.abs(headingError); + if (absHeadingError < robotType.tolerance) { + return 0; + } + } + + // compute the PID output given current headingError + radiusPidOutput = radiusPid.computePid(0, headingError); + spinSpeed = (int)radiusPidOutput; + if (spinSpeed > 50) spinSpeed = 50; // cap spinSpeed + if (spinSpeed < -50) spinSpeed = -50; // cap spinSpeed + System.out.print(" rPidOut " + radiusPidOutput ); + + // start spinning + robot.spin(spinSpeed); + + // check for user stop input + if (System.in.available() != 0){ + robot.stop(); + return -4; + } + elapsedTime = System.currentTimeMillis() - startTime; + System.out.println(" in " + elapsedTime + "ms"); + + // spin for 100ms + // Thread.sleep(100); + } catch (Exception e) { + e.printStackTrace(); + return -3; + } + } + } + + public int driveByCompass(double distance) throws Exception + { + int rv; + int direction; + + direction = arduino.getCompass(); + if (direction < 0) { + System.out.println("Read invalid heading: " + direction); + robot.stop(); + return -3; + } + rv = driveByCompass(distance, direction); + return rv; + } + + public int driveByCompass(double distance, int direction) throws Exception + { + // distance control variables + double distanceTravelled = 0; + double startEncDistance = 0; + double readEncDistance = 0; + double distanceToGo = 0; + int heading = 0; + int headingError = 0; + // speed control variables: speedRamp is a table of {speed, brakingDistance} + final int [][] speedRamp = {{20,2},{60,2},{100,2},{140,3},{180,4},{220,9},{260,12},{300,15},{340,19},{380,23},{420,27},{460,33},{500,38}}; + final int maxRamp = 12; // number of steps in speed ramp + int rampIndex = 0; + boolean rampUp = true; // start by ramping up + int currentSpeed; + // direction control variables + int [] radiusTable; + int radiusTableMiddle; // define the midpoint around which PID swings us + int radiusTableIndx, radius; + double radiusPidOutput = 0; + + readEncoderDistance(true); // initialize the encoder reader + while (direction < 0) { + System.out.println("getting direction to head in"); + direction = arduino.getCompass(); + if (direction < 0) { + System.err.println("Error reading compass"); + } + } + System.out.println("Driving " + distance + " inches on heading " + direction); + // initialize the distance control system + if (distance <= 0) return -1; + distanceToGo = distance; + readEncDistance = startEncDistance = readEncoderDistance(false); + if (startEncDistance < 0) { + System.out.println("FIXME Error reading encoders in driveByCompass()"); + readEncDistance = startEncDistance = readEncoderDistance(false); // read it a 2nd time + if (startEncDistance < 0) { + System.out.println("Second error reading encoders in driveByCompass()"); + return -2; + } + } + currentSpeed = speedRamp[rampIndex][0]; + //System.out.println("Encoders (L, R): " + lEncoder +" " + rEncoder); + + // initialize the direction control system + radiusTable = initRadiusTable(); + radiusTableMiddle = (radiusTable.length/2); // table is always an odd # of elements, point to middle of table + radiusTableIndx = radiusTableMiddle; // initial radius is middle of table (straight) + radius = radiusTable[radiusTableIndx]; + Pid radiusPid = new Pid(robotType.KP, robotType.KI, robotType.KD); + System.out.printf("Distance: %4.2f to go: %4.2f Speed: %d, ReadEncDist: %4.2f (%d %d) radiusPidOutput: %4.2f Radius %d\n", + distanceTravelled, distanceToGo, currentSpeed, readEncDistance, lEncoder, rEncoder, radiusPidOutput, radius); + + // turn to point in the right direction + spinToHeading(direction); + robot.pause(500); + + // drive checking sensors every 100ms + while (true) { + robot.drive(currentSpeed, radius); + Thread.sleep(100); // drive for 100ms + + // get encoders & compute distance remaining + readEncDistance = readEncoderDistance(false); + if (readEncDistance < 0) { + System.err.println("FIXME Error reading encoders in driveByCompass()"); + readEncDistance = readEncoderDistance(false); // read it a 2nd time + if (startEncDistance < 0) { + System.out.println("Second error reading encoders in driveByCompass()"); + continue; + } + } + distanceTravelled = readEncDistance - startEncDistance; + distanceToGo = distance - distanceTravelled; + heading = arduino.getCompass(); + if (heading < 0) { + System.err.println("Error reading compass"); + continue; + } + //System.out.println("Encoders (L, R, distance): " + lEncoder +" " + rEncoder + " " + currentDistance); + + // ramp speed up then down + if (distanceToGo < speedRamp[rampIndex][1]) { // if closer than braking distance, decelerate to Vmin + rampUp = false; + if (rampIndex > 0) + rampIndex--; + } else if (distanceToGo > speedRamp[rampIndex][1]) { // if further away than braking distance, accelerate to Vmax + if ((rampIndex < maxRamp) && (rampUp == true)) + rampIndex++; + } + currentSpeed = speedRamp[rampIndex][0]; + + heading = arduino.getCompass(); + headingError = headingChange(direction, heading); + radiusPidOutput = radiusPid.computePid(0, headingError); + radiusTableIndx = (int)radiusPidOutput + radiusTableMiddle; // offset PID output into table + if (radiusTableIndx < 0) radiusTableIndx = 0; + else if (radiusTableIndx >= radiusTable.length) radiusTableIndx = radiusTable.length -1; + radius = radiusTable[radiusTableIndx]; + + //System.out.printf("Distance: %4.2f to go: %4.2f Heading: %d Speed: %d, ReadEncDist: %4.2f (%d %d) radiusPidOutput: %4.2f Radius %d\n", + // distanceTravelled, distanceToGo, heading, currentSpeed, readEncDistance, lEncoder, rEncoder, radiusPidOutput, radius); + + // stop if we've arrived + if ((distance < distanceTravelled) || (System.in.available() > 0) || (readEncDistance < 0)) { + robot.stop(); + System.out.println("Finished trip, travelled: " + distanceTravelled + " in, readEncDistance: " + readEncDistance); + robot.pause(500); // pause to let motion stop, then recheck distance + break; + } + } + return 0; + } + + /** + * Drive to a target that has been previously set using the settarget command. + * Use the localizer to decide how far & which direction to drive + */ + private void seekToTarget() { + int currentHeading; + while ((currentHeading = arduino.getCompass()) < 0) + ; + seekToTarget(currentHeading, 0); + + } + private void seekToTarget(int startHeading, long timeLimit) { + // measure/compute initial distance & direction to target + try { + while (!localize()) + robot.goForward(12 * 25); // go forward a foot + vectorToTarget(); // compute the distance and course to target + } catch (Exception e) { + System.err.println("Exception getting vector to target\n" + e.getMessage()); + return; + } + + // seek to target while further away than tolerance, limited by timeLimit if non-zero + while ((distanceToTarget > 2.0) && ((timeLimit == 0) || (timeLimit > System.currentTimeMillis()))) { + int rv = spinToHeading(courseToTarget); + if (rv < 0) { + System.out.println("Error spinning to target: " + rv); + return; + } + try { + rv = driveByCompass(distanceToTarget, courseToTarget); + if (rv < 0) { + System.out.println("Error driving to target: " + rv); + } + } catch (Exception e) { + System.out.println("Error driving to target: " + e.getMessage()); + e.printStackTrace(); + return; + } + // measure/compute current distance & direction to target + try { + while (!localize()) + robot.goForward(12 * 25); // go forward a foot + vectorToTarget(); // compute the distance and course to target + } catch (Exception e) { + System.err.println("Exception getting vector to target\n" + e.getMessage()); + return; + } + } + spinToHeading(startHeading); + if (timeLimit > System.currentTimeMillis()) { + System.out.println("**** Timed out while seeking target ****"); + } else { + System.out.println("**** Arrived at target ****"); + } + return; + } + + /** + * Drive in a rectangle specified by length (1st & 3rd legs) & width (2nd & 4th legs) with optional heading + */ + private void rectangle() { + int length, width; + + if (args.length < 3) { + System.out.println("Error: must specify length, width"); + return; + } + length = Integer.parseInt(args[1]); + width = Integer.parseInt(args[2]); + if (args.length == 4) + angle = Integer.parseInt(args[3]); + else + angle = arduino.getCompass(); // if initial heading not specified, use current heading + if (angle < 0) { + System.out.println("initial heading must be > 0, was: " + angle); + return; + } + System.out.println("Driving rectangle length: " + length + " width: " + width + "initial heading: " + angle); + try { + for (int i=0;i<2;i++) { + driveByCompass(length, angle); + angle += 90; + angle %= 360; + driveByCompass(width, angle); + angle += 90; + angle %= 360; + } + spinToHeading(angle); + } catch (Exception e) { + System.out.println("Exception driving"); + e.printStackTrace(); + } + return; + } + + /** + * Drive in a square specified by length with optional heading + */ + private void squareDance() { + int length; + int startHeading; + + if (args.length < 2) { + System.out.println("Error: must specify length of side"); + return; + } + length = Integer.parseInt(args[1]); + startHeading = arduino.getCompass(); // record what direction to turn to at finish + + if (args.length == 3) + startHeading = angle = Integer.parseInt(args[2]); + else + angle = startHeading; // if initial heading not specified, use current heading + if (angle < 0) { + System.out.println("initial heading must be > 0, was: " + angle); + return; + } + System.out.println("Driving square dance length: " + length + "initial heading: " + angle); + startTime = System.currentTimeMillis(); + long timeLimit = System.currentTimeMillis() + (2500 * 60); // limit square dance to 2.5 minutes to avoid running overtime + + try { + // drive 4 sides + for (int i=0;i<4;i++) { + driveByCompass(length, angle); + angle += 90; + angle %= 360; + } + + seekToTarget(startHeading, timeLimit); + + } catch (Exception e) { + System.out.println("Exception driving"); + e.printStackTrace(); + } + spinToHeading(startHeading); + return; + } + + /* + * Drive squaredance just based on time, without seek to target at end. + * Don't use compass to control direction, no timeLimit + */ + private void squareDance2() { + int length; + int startHeading; + + if (args.length < 2) { + System.out.println("Error: must specify length of side"); + return; + } + length = Integer.parseInt(args[1]); + startHeading = arduino.getCompass(); // record what direction to turn to at finish + + if (args.length == 3) + startHeading = angle = Integer.parseInt(args[2]); + else + angle = startHeading; // if initial heading not specified, use current heading + if (angle < 0) { + System.out.println("initial heading must be > 0, was: " + angle); + return; + } + System.out.println("Driving square dance length: " + length + "initial heading: " + angle); + startTime = System.currentTimeMillis(); + + try { + // drive 4 sides + robot.speed =200; + for (int i=0;i<4;i++) { + robot.goStraight(length * 25); + angle += 90; + angle %= 360; + spinToHeading(angle); + } + } catch (Exception e) { + System.out.println("Exception driving"); + e.printStackTrace(); + } + spinToHeading(startHeading); + return; + + } + /* + * Drive squaredance just based on time, without seek to target at end. + * Don't use compass to control direction, no timeLimit + */ + private void squareDance3() { + int length; + int startHeading; + + if (args.length < 2) { + System.out.println("Error: must specify length of side"); + return; + } + length = Integer.parseInt(args[1]); + startHeading = arduino.getCompass(); // record what direction to turn to at finish + + if (args.length == 3) + startHeading = angle = Integer.parseInt(args[2]); + else + angle = startHeading; // if initial heading not specified, use current heading + if (angle < 0) { + System.out.println("initial heading must be > 0, was: " + angle); + return; + } + System.out.println("Driving square dance length: " + length + "initial heading: " + angle); + startTime = System.currentTimeMillis(); + long timeLimit = System.currentTimeMillis() + (2500 * 60); // limit square dance to 2.5 minutes to avoid running overtime + + try { + // drive 4 sides + robot.speed =200; + for (int i=0;i<4;i++) { + robot.goStraight(length * 25); + angle = arduino.getCompass(); + angle += 90; + angle %= 360; + spinToHeading(angle); + } + + } catch (Exception e) { + System.out.println("Exception driving"); + e.printStackTrace(); + } + seekToTarget(startHeading, timeLimit); + + + spinToHeading(startHeading); + return; + + } + + /** + * Drive to edge of table, back up, turn around, & do it to the other end, but don't fall off + */ + private void tabletrip() + { + robot.goForwardAt(100); + robot.pause(15000); + robot.full(); + robot.pause(500); + robot.goBackward(50); + robot.safe(); + robot.pause(500); + System.out.println("safe, spinning"); + spinByCompass(180); + robot.goForwardAt(100); + robot.pause(15000); + robot.full(); + robot.pause(500); + robot.goBackward(50); + robot.safe(); + System.out.println("safe, spinning"); + robot.pause(500); + spinByCompass(180); + } + + /** + * RoboColumbus: drive a distance, turn on video & snap pic, find target in image using roborealm, & incrementally seek + * to target, re-acquiring image every so often + */ + private void robocolumbus() + { + int initialDriveDistance = 12; + int startHeading; + boolean rv; + + // set up parameters + if (args.length < 1) { + System.out.println("Error: must specify drive distance (feet) before seeking target"); + return; + } + //initialDriveDistance = Integer.parseInt(args[1]) * 12; + startHeading = arduino.getCompass(); // record what direction to face when searching for target + + angle = 83; + //angle = ; + initialDriveDistance = 95; + if (args.length == 2) { + initialDriveDistance = 12 * Integer.parseInt(args[1]); + } + if (args.length == 3) + startHeading = angle = Integer.parseInt(args[2]); + else + angle = startHeading; // if initial heading not specified, use current heading + if (angle < 0) { + System.out.println("initial heading must be > 0, was: " + angle); + return; + } + + + System.out.println("Driving RoboColumbus length: " + initialDriveDistance + "initial heading: " + angle); + startTime = System.currentTimeMillis(); + long timeLimit = System.currentTimeMillis() + (4500 * 60); // limit square dance to 2.5 minutes to avoid running overtime + + // start driving + try { + // drive to image search point + spinToHeading(angle); + robot.speed = 200; + driveByCompass(initialDriveDistance*12, angle); + + // point to the start direction for the image search + spinToHeading(angle); + + rv = seekToVisualTarget(timeLimit); + if (!rv) { + System.out.println("FAILED: unable to visually acquire target - stopping"); + System.exit(-1); + } + } catch (Exception e) { + System.out.println("Exception driving"); + e.printStackTrace(); + } + return; + + } + + private boolean seekToVisualTarget(long timeLimit) + { + double incrementalDistance = 12.0; + boolean arrived = false; + int currentSeekHeading; + + currentSeekHeading = arduino.getCompass(); // starting direction for looking for targe + // measure/compute initial distance & direction to target + + while (!arrived) { + currentSeekHeading = pointToVisualTarget(); // update current heading to target + try { + if (System.in.available() != 0){ + robot.stop(); + return false; + } + } catch (Exception e) { + System.out.println("Exception: in System.in.available"); + System.exit(-1); + } + + int rv = spinToHeading(currentSeekHeading); + if (rv < 0) { + System.out.println("Error spinning to target: " + rv); + return false; + } + try { + rv = driveByCompass(incrementalDistance, currentSeekHeading); + if (rv < 0) { + System.out.println("Error driving to target: " + rv); + } + } catch (Exception e) { + System.out.println("Error driving to target: " + e.getMessage()); + e.printStackTrace(); + return false; + } + } + return true; + } + + /* + * Point robot at visual target. On exit, provide compass heading to target, and robot is pointed + * approximately at target + * @param currentHeading The starting direction we're approximately facing in, and the direction + * around which the algorithm searches for the target + */ + public int pointToVisualTarget() + { + int targetHeading; + int currentSeekHeading, startHeading; + int currentSeekHeadingIncrement = 5; + int seekIteration; + final int seekLimit = 24; // how many seeks until we give up + boolean gotDataFlag; + + currentSeekHeading = arduino.getCompass(); + startHeading = currentSeekHeading; + + // seek back and forth until we acquire image with good confidence + for (seekIteration=1; seekIteration 359) + return (heading % 360); + else if (heading < 0) + return (heading + 360); + return heading; + } + + /* + * Ask the localizer for robot location. Results returned in x, y class variables + * @return boolean which is false if localization failed, true otherwise + */ + private boolean localize() + { + String locationString = ""; + + + if (localizerConnection == null) { + // if no localizer, prompt for a location + System.out.println("Enter location in feet (decimals ok), x and y, in form like x 1.2 2.9"); + try { + locationString = in.readLine(); + } catch (IOException e) { + // Print out the exception that occurred + System.out.println("Error reading line: " + e.getMessage()); + return false; + } + } else { + // we are connected to localizer, ask it for location + localizerConnection.send('L'); // send localize command to localizer + try { + locationString = localizerConnection.readBotToTerminator(ReadTerminator.NULL); + } catch (Exception e) { + System.out.println("Error: exception reading localizer"); + return false; + } + } + + // split the string apart & look for the Invalid keyword + System.out.println("received string: " + locationString); + String [] splitLocationStrings = locationString.split("\\s"); + if (splitLocationStrings[0].compareTo("bogus") == 0) { + System.out.println("Location is invalid"); + return false; + } + + // parse the location string into x & y and print + if (splitLocationStrings.length < 3) { + System.out.println("Error: must have at least 3 elements in location string"); + return false; + } + try { + x = new Double(splitLocationStrings[1]) * 12; + y = new Double(splitLocationStrings[2]) * 12; + } catch (Exception e) { + System.err.println("Error parsing location strings\n" + e.getMessage()); + return false; + } + System.out.println("x: " + x + " y: " + y); + return true; + } + private void saveTarget(String x, String y) + { + // target is entered in decimal feet, usually from localizer reading + targetX = Double.parseDouble(x) * 12; + targetY = Double.parseDouble(y) * 12; + try { + arduino.writeConfigDouble(new String("targetX"), targetX); + arduino.writeConfigDouble(new String("targetY"), targetY); + targetInitialized = true; + } catch (Exception e) { + System.err.println("Error writing target location"); + } + } + public void vectorToTarget () throws Exception + { + double deltaX, deltaY; + double localizerDirection = 0; + int angle2t; + + // if we haven't initialized the target location yet, try to read from a config file + // targetInitialized ensures we only try this once + try { + if (!targetInitialized) { // get the compass offset from file if it exists + targetX = arduino.readConfigDouble(new String("targetX")); + targetY = arduino.readConfigDouble(new String("targetY")); + System.out.println("target set by file read to x: " + targetX + " y: " + targetY); + targetInitialized = true; + } + if (!localizerOffsetInitialized) { // get the localizer Y axis offset from North if it exists + localizerCompassOffset = arduino.readConfigInt("localizerCompassOffset"); + localizerOffsetInitialized = true; + System.out.println("localizerCompassOffset set by file to " + localizerCompassOffset); + } + } catch (Exception e) { + System.out.println("No targetX or targetY file or localizer compass offset file could be opened\n" + e.getMessage()); + throw (e); + } + + // compute distance from current location to target + deltaX = targetX - x; + deltaY = targetY - y; + distanceToTarget = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); + if (deltaX == 0) { // make sure we don't try to divide by zero + if (deltaY > 0) + localizerDirection = Math.PI/2; + else + localizerDirection = -Math.PI/2; + } else { // deltaX is non-zero, compute arctan to find angle to target + localizerDirection = Math.atan(deltaY/deltaX); // in radians, with incrementing positive values going counterclockwise + if ((deltaX < 0) && (deltaY <=0)) localizerDirection -= Math.PI; + if ((deltaX < 0) && (deltaY > 0)) localizerDirection += Math.PI; + } + angle2t = (int)Math.toDegrees(localizerDirection); // angle to start in localizer frame of reference, +180 to -180 + courseToTarget = 90 - angle2t + localizerCompassOffset; // convert to +ve Y axis = 0 degrees, increasing angle clockwise + courseToTarget %= 360; // make sure course < -360 + if (courseToTarget < 0) courseToTarget += 360; // turn -90 into 270 + System.out.println("** Current location: x: " + x + " y: " + y + " Target location x: " + targetX + " y: " + targetY); + System.out.println("** Distance to target: " + distanceToTarget + " heading: " + courseToTarget); + System.out.println("localizerCompassOffset: " + localizerCompassOffset); + } + + private int [] initRadiusTable() + { + // speed table goes in steps of 100 from -500 to -2000, with 0x8000 instead of 0 + // then 2000 to 500 + int stepSize = 100; + int minRadius = 500; + int maxRadius = 2000; + int numEntries = (((maxRadius-minRadius) * 2)/stepSize) + 3; // +1 for straight, +1 on each side for inclusive entries + int [] radiusTable = new int[numEntries]; + int indx = 0; + int tableVal = -500; + + do { + radiusTable[indx++] = tableVal; + tableVal -= stepSize; // -500, -600 ... -2000 + } while (tableVal >= -maxRadius); + tableVal = 0x8000; // mid-value is 0x8000 + radiusTable[indx++] = tableVal; + tableVal = maxRadius; + do { + radiusTable[indx++] = tableVal; + tableVal -= stepSize; // 2000, 1900, ... 500 + } while (tableVal >= 500); + + return radiusTable; + } + /** + * + * @param init If true, init initializes encoder variables, otherwise regular read encoder distance + * @return Average of encoders distance, in inches + * @throws Exception + */ + public double readEncoderDistance(boolean init) throws Exception + { + byte [] encoders = {43, 44}; + byte [] sensor_bytes; + double encoderDistance; + + if ((protocol.equalsIgnoreCase("AR")) || (protocol.equalsIgnoreCase("MO"))) { + // FIXME need to handle overflow + encoderDistance = arduino.getEncoders(); + lEncoder = rEncoder = arduino.getEncoder(); + } else { + // WARNING - THIS DOESN'T SEEM TO HANDLE OVERFLOW WELL - WON'T WORK WITH TANKBOT + // read encoders from roomba + robot.queryList(encoders, 4); + if (robot.getSensorData(4) == false) // read failed + return -1; + sensor_bytes = robot.getSensor_bytes(); + + // handle overflow in encoders + lastLEncoder = lEncoder; // first time through will be 0, then will be last reading; + lEncoder = ArduinoBot.toUnsignedShort(sensor_bytes[0], sensor_bytes[1]); + lEncoder += lEncoderOverflow; + lastREncoder = rEncoder; + rEncoder = ArduinoBot.toUnsignedShort(sensor_bytes[2], sensor_bytes[3]); + rEncoder += rEncoderOverflow; + + if (init) { + lEncoderOverflow = 0; + rEncoderOverflow = 0; + lEncoder = ArduinoBot.toUnsignedShort(sensor_bytes[0], sensor_bytes[1]); + rEncoder = ArduinoBot.toUnsignedShort(sensor_bytes[2], sensor_bytes[3]); + lastLEncoder = lEncoder; // initialize last value to current reading + lastREncoder = rEncoder; + } else { + if ((lEncoder - lastLEncoder) < -1000) { + lEncoderOverflow += 1<<16; // add 65536 to overflow adjuster if encoder rolled over + lEncoder += 1<<16; + } + if ((rEncoder - lastREncoder) < -1000) { + rEncoderOverflow += 1<<16; // add 65536 to overflow adjuster if encoder rolled over + rEncoder += 1<<16; + } + } + // average the encoders & turn into inches + } + + encoderDistance = ((lEncoder + rEncoder)/2) /robotType.countsPerInch; + //System.out.println("encoderDistance: " + encoderDistance + " lEncoder: " + lEncoder + " rEncoder: " + rEncoder + " lastLEncoder: " + lastLEncoder + " lEncoderOverflow: " + lEncoderOverflow + // + " lastREncoder: " + lastREncoder + " rEncoderOverflow: " + rEncoderOverflow); + return encoderDistance; + } + + /** + * Calculate the shortest heading change which will move the bot from current heading to target. + * See http://www.dreamincode.net/forums/topic/163469-calculating-the-difference-between-two-angles/ + * for some concepts. + * + * @param target Desired compass heading: 0 - 359 degrees + * @param heading Current compass heading: 0 - 359 degrees + * @return direction and angle to turn. Positive is clockwise (like a compass) + */ + public int headingChange(int target, int heading) + { + int diff = target - heading; + if (diff > 180) diff -= 360; + else if (diff < -180) diff += 360; + return diff; + } + private void testHeadingChange() + { + int heading[] = {35, 45, 315, 45, 0, 180, 45}; + int target[] = {45, 35, 45, 315, 180, 0, 45}; + int diff; + int testCaseCount = 7; + for (int i=0; i + * This method uses the value of protocol to set the connection to the correct Roomba API version.
+ * This method uses the value of handshakeButton and portChoices to make the serial connection.
+ * This method also "chirps" the Roomba when it connects to provide an audio feedback/confirmation of the connection. + * + * @return true, if successful + */ + public boolean connect() { + debugPrintln(debug,"connect-start"); + String portname = (String) portChoices.getSelectedItem(); + roombaCommSerial.setWaitForDSR(handshakeButton.isSelected()); + int i = protocolChoices.getSelectedIndex(); + roombaCommSerial.setProtocol((i == 0) ? "SCI" : "OI"); + if (portname == null){ + // we do not yet have ports so refresh the list + setCommPorts(); + } + updateDisplay("connecting to " + portname + "\n"); + connectButton.setText("connecting"); + if (portname != null){ + if (!roombaCommSerial.connect(portname)) { + updateDisplay("Couldn't connect to " + portname + "\n"); + connectButton.setText(" connect "); + debugPrintln(debug,"connect-end (could not connect)"); + return false; + }else{ + updateDisplay("connected to " + portname + "\n"); + } + }else{ + updateDisplay("you must first select a COMM port to use before you can attempt to connect"); + jTabbedPanelConfig.setSelectedIndex(1); + return false; + } + updateDisplay("Roomba startup\n"); + + roombaCommSerial.startup(); + roombaCommSerial.control(); + roombaCommSerial.playNote(72, 10); // C , test note + roombaCommSerial.pause(200); + + connectButton.setText("disconnect"); + connectButton.setActionCommand("disconnect"); + roombaComm = roombaCommSerial; + updateDisplay("Checking for Roomba... "); + if (roombaCommSerial.updateSensors()) { + updateDisplay("Roomba found!\n"); + debugPrintln(debug,"connect-end"); + return true; + }else{ + updateDisplay("No Roomba. :( Is it turned on?\n"); + debugPrintln(debug,"connect-end"); + return true ; + } + } + + /** + * Connect to a Roomba using a network connection.
+ * This method uses the value of protocol to set the connection to the correct Roomba API version.
+ * This method uses the value of host and port to make the network connection.
+ * This method also "chirps" the Roomba when it connects to provide an audio feedback/confirmation of the connection. + * + * @param portname the portname + * @return true, if successful + */ + public boolean connectNet(String portname) { + debugPrintln(debug,"connectNet called"); + int i = protocolChoices.getSelectedIndex(); + if (roombaCommTCPClient == null){ + roombaCommTCPClient = new RoombaCommTCPClient(); + roombaCommTCPClient.setConnected(false); + } + roombaCommTCPClient.setProtocol((i == 0) ? "SCI" : "OI"); + if (!roombaCommTCPClient.connect(portname)) { + updateDisplay("Couldn't connect to " + portname); + return false; + } + updateDisplay("Roomba startup on port " + portname); + roombaCommTCPClient.startup(); + roombaCommTCPClient.control(); + // roombacomm.setSensorsAutoUpdate(true); + roombaCommTCPClient.pause(30); + //TODO: I am not sure this works yet... need to test to confirm. (2010.03.03) + //TODO: The commented block below did not work.. so I commented it and hardcoded a "true" for the connect + updateDisplay("Checking for Roomba... \n"); + roombaCommTCPClient.setConnected(true); +// if (roombaCommTCPClient.updateSensors()) { +// updateDisplay("Roomba found!\n"); +// updateDisplay(roombaCommTCPClient.getSensorsAsString()); +// } else { +// updateDisplay("No Roomba. :( Is it turned on?\n"); +// } + updateDisplay("buffer is(" + roombaCommTCPClient.getBuffer() + ")", + this.debug); + updateDisplay("connected (" + roombaCommTCPClient.connected() + ")\n"); + if (roombaCommTCPClient.connected()) { + updateDisplay("Playing some notes\n"); + roombaCommTCPClient.playNote(72, 10); // C + roombaCommTCPClient.pause(200); + roombaCommTCPClient.playNote(79, 10); // G + roombaCommTCPClient.pause(200); + roombaCommTCPClient.playNote(76, 10); // E + roombaCommTCPClient.pause(200); + netButton.setText("disconnect-net"); + netButton.setActionCommand("disconnect-net"); + roombaComm = roombaCommTCPClient; // set the pointer so that the + // action stuff can work against + // any class + } + return true; + } + + /** + * Disconnect from the roomba when it is connected via a serial connection. + */ + public void disconnect() { + roombaCommSerial.disconnect(); + connectButton.setText(" connect "); + connectButton.setActionCommand("connect"); + } + + /** + * Disconnect from the roomba when it is connected via a network connection. + */ + public void disconnectNet() { + roombaCommTCPClient.disconnect(); + netButton.setText(" net "); + netButton.setActionCommand("net"); + } + + /** + * Play a (MIDI) note, that is, make the Roomba a musical instrument
+ * notenums 32-127:
notenum == corresponding note played thru beeper
+ * velocity == duration in number of 1/64s of a second (e.g. 64==1 second)
+ * notenum 24: notenum == main vacuum velocity == non-zero turns on, zero + * turns off
notenum 25: blink LEDs, velcoity is color of Power
notenum 28 & 29: spin left & spin right, velocity is speed + * + * @param notenum 32-127
corresponding note played thru beeper
+ * @param velocity duration in number of 1/64s of a second (e.g. 64==1 second) + */ + public void playMidiNote(int notenum, int velocity) { + updateDisplay("play note: " + notenum + "," + velocity + "\n"); + if (!roombaCommSerial.connected()) + return; + + if (notenum >= 31) { // G and above + if (velocity == 0) + return; + if (velocity < 4) + velocity = 4; // has problems at lower durations + else + velocity = velocity / 2; + roombaCommSerial.playNote(notenum, velocity); + } else if (notenum == 24) { // C + roombaCommSerial.vacuum(!(velocity == 0)); + } else if (notenum == 25) { // C# + boolean lon = (velocity != 0); + int inten = (lon) ? 255 : 128; // either full bright or half bright + roombaCommSerial.setLEDs(lon, lon, lon, lon, lon, lon, + velocity * 2, inten); + } else if (notenum == 28) { // E + if (velocity != 0) + roombaCommSerial.spinLeftAt(velocity * 2); + else + roombaCommSerial.stop(); + } else if (notenum == 29) { // F + if (velocity != 0) + roombaCommSerial.spinRightAt(velocity * 2); + else + roombaCommSerial.stop(); + } + } + + /** + * implement actionlistener. + * + * @param event the event + */ + public void actionPerformed(ActionEvent event) { + String action = event.getActionCommand(); + updateDisplay(formatter.format(new Date()) + ": action (" + action + + ") happened\n", this.debug); + if ("comboBoxChanged".equals(action)) { + int i = protocolChoices.getSelectedIndex(); + if (roombaComm != null) { + roombaComm.setProtocol((i == 0) ? "SCI" : "OI"); + } else { + updateDisplay( + formatter.format(new Date()) + + ": null roombaComm object found in actionPerformed\n", + this.debug); + } + return; + } + if ("net".equals(action)) { + if (jTextFieldHost != null && jTextFieldHost.getText() != null && jTextFieldPort != null && jTextFieldPort.getText() != null){ + if(connectNet(jTextFieldHost.getText()+":"+jTextFieldPort.getText())){ + // TODO: find a way to hide/disable the other connect tab until the session is disconnected +// getJTabbedPanelConfig().getComponentAt(1).setVisible(false); // hide the Serial tab while we are connected with a Net port + }else{ + // TODO: reenable/show the other tab(s) +// getJTabbedPanelConfig().getComponentAt(0).setVisible(true); // make sure the net tab is not hidden when we fail to connect + } + }else{ + updateDisplay("connect (via net) pressed with missing values:"); + if (jTextFieldHost != null && jTextFieldHost.getText() != null){ + updateDisplay(" host :"+jTextFieldHost.getText()); + }else{ + updateDisplay(" host : MISSING VALUE"); + } + if (jTextFieldPort != null && jTextFieldPort.getText() != null){ + updateDisplay(" port :"+jTextFieldPort.getText()); + }else{ + updateDisplay(" port : MISSING VALUE"); + } + } + return; + } else if ("disconnect-net".equals(action)) { + disconnectNet(); + // TODO: reenable/show the other tab(s) +// getJTabbedPanelConfig().getComponentAt(1).setVisible(true); // show the serial tab while we disconnect from the net + return; + } + if ("connect".equals(action)) { + if (connect()){ + // TODO: find a way to hide/disable the other connect tab until the session is disconnected + }else{ + // TODO: reenable/show the other tab(s) +// getJTabbedPanelConfig().getComponentAt(0).setEnabled(true); // make sure the net tab is not hidden when we fail to connect +// getJTabbedPanelConfig().repaint(100); + } + return; + } else if ("disconnect".equals(action)) { + disconnect(); + // TODO: reenable/show the other tab(s) +// getJTabbedPanelConfig().getComponentAt(0).setEnabled(true); // show the net tab while we disconnect from the serial + return; + } + // stop right here if we're not connected + if (roombaComm == null || !roombaComm.connected()) { + updateDisplay("not connected!\n"); + return; + } + + if ("stop".equals(action)) { + roombaComm.stop(); + } else if ("forward".equals(action)) { + // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n"); + roombaComm.goForward(); + } else if ("backward".equals(action)) { + // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n"); + roombaComm.goBackward(); + } else if ("spinleft".equals(action)) { + // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n"); + roombaComm.spinLeft(); + } else if ("spinright".equals(action)) { + // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n"); + roombaComm.spinRight(); + } else if ("turnleft".equals(action)) { + roombaComm.turnLeft(); + } else if ("turnright".equals(action)) { + roombaComm.turnRight(); + } else if ("max".equals(action)) { + roombaComm.max(); + } else if ("dock".equals(action)) { + roombaComm.dock(); + } else if ("test".equals(action)) { + LogoA.square(roombaComm, 300); + } else if ("OSU".equals(action)) { + updateDisplay("Going to play OSU\n"); + roombaComm.stop(); + roombaComm.pause(500); + playSong( + roombaComm, + "OSU:d=4,o=5,b=125:a,g,a#,a,8g#,a,8g#,2a,8p,8f,8g,8g#,a,8g#,a,8g#,a,g,f,a,g,8a,g,d,8f,8p,8f,8p,8f,8p,8f,8p,c6,a,g,f,8a#,a,8g,f,p,c6,a,g,a,8a#,a,8a#,c6,p,2d6,d,8c6,a#,g,f,8f,8f,8g,a#,8g,a#,a,2a#"); + // playSong(roombacomm,"Baa Baa Black Sheep:d=4,o=5,b=125:c,c,g,g,8a,8b,8c6,8a,g,p,f,f,e,e,d,d,c"); + roombaComm.stop(); + } else if ("Play RTTL".equals(action)){ + updateDisplay("Going to prompt for rttl string...\n"); + String rttl =""; + updateDisplay("Going to play rttl string ("+rttl+")...\n"); + } else if ("Tribble On".equals(action)) { + tribbleOn = true; + tribbleStart(roombaComm, displayText, tribbleOn); + } else if ("reset".equals(action)) { + roombaComm.stop(); + roombaComm.startup(); + roombaComm.control(); + } else if ("passive".equals(action)) { + //passive / start command + roombaComm.start(); + } else if ("safe".equals(action)) { + roombaComm.safe(); + } else if ("full".equals(action)) { + roombaComm.full(); + } else if ("power-off".equals(action)) { + roombaComm.powerOff(); + } else if ("power-on".equals(action)) { + roombaComm.powerOn(); + } else if ("wakeup".equals(action)) { + roombaComm.wakeup(); + } else if ("beep-lo".equals(action)) { + roombaComm.playNote(50, 32); // C1 + roombaComm.pause(200); + } else if ("beep-hi".equals(action)) { + roombaComm.playNote(90, 32); // 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"); + } else if ("chargedata".equals(action)) { + if (roombaComm.updateSensors()) + updateDisplay("*****\n" + roombaComm.chargeDataAsString()+ "\n"); + else + updateDisplay("couldn't read Roomba. Is it connected?\n"); + } else if ("toggleGreen".equals(action)) { + setChgGreenLED(roombaComm, !greenOn); + } else if ("toggleRed".equals(action)) { + setChgRedLED(roombaComm, !redOn); + } else if ("toggleSpot".equals(action)) { + setChgSpotLED(roombaComm, !toggleSpot); + } else if ("toggleClean".equals(action)) { + setChgCleanLED(roombaComm, !toggleClean); + } else if ("toggleMax".equals(action)) { + setChgMaxLED(roombaComm, !toggleMax); + } else if ("toggleDirt".equals(action)) { + setChgDirtLED(roombaComm, !toggleDirt); + } else if ("toggleCheckRobot".equals(action)) { + setToggleCheckRobot(roombaComm, !toggleCheckRobot); + } else if ("toggleDock".equals(action)) { + setToggleDock(roombaComm, !toggleDock); + } + } + + /** + * implement ChangeListener, for the slider. + * + * @param e the e + */ + public void stateChanged(ChangeEvent e) { + // System.err.println("stateChanged:"+e); + JSlider src = (JSlider) e.getSource(); + if (!src.getValueIsAdjusting()) { + int speed = (int) src.getValue(); + speed = (speed < 1) ? 1 : speed; // don't allow zero speed + if (roombaComm != null){ + updateDisplay("setting speed = " + speed + "\n"); + roombaComm.setSpeed(speed); + } + } + } + + /** + * TODO: comment needed for makePanels. + */ + void makePanels() { + debugPrintln(debug,"makePanels-start"); + getContentPane().add(getJPanel4xx(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2)); + makeDisplayPanel(); + updateDisplay("RoombaComm, version " + RoombaComm.VERSION + "\n"); + debugPrintln(debug,"makePanels-finish"); + } + + /** + * TODO: need comment for makeDisplayPanel. + */ + void makeDisplayPanel() { + JTextArea displayText2 = new JTextArea(25, 30); + displayText2.setLineWrap(true); + DefaultCaret dc = new DefaultCaret(); + dc.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); + } + + /** + * Update display. + * + * @param s the s + */ + protected void updateDisplay(String s) { + debugPrintln(debug,"updateDisplay(string): start"); + displayText.append(s); + if (s != null && !(s.endsWith("\n"))) { + displayText.append("\n"); + } + debugPrintln(debug,"updateDisplay(string): reposition to "+displayText.getDocument().getLength()); + displayText.getParent().validate(); + displayText.getParent().repaint(100); + displayText.setText(displayText.getText() ); + debugPrintln(debug,"updateDisplay(string): end"); + } + + /** + * Update display. + * + * @param s the s + * @param onlyDebug the only debug + */ + protected void updateDisplay(String s, boolean onlyDebug) { + if (onlyDebug && (roombaCommSerial.debug || roombaCommTCPClient.debug)) { + updateDisplay(s); + debugPrintln(true,s); + } + } + + /** + * Update display. + * + * @param roombacomm the roombacomm + * @param jtextarea the jtextarea + * @param s the s + * @param onlyDebug the only debug + */ + protected static void updateDisplay(RoombaComm roombacomm, + JTextArea jtextarea, String s, boolean onlyDebug) { + if (onlyDebug && roombacomm.debug) { + jtextarea.append(s); + jtextarea.setCaretPosition(jtextarea.getDocument().getLength()); + jtextarea.getParent().validate(); + jtextarea.getParent().repaint(100); + System.out.println(s); + } + } + + /** + * Returns an ImageIcon, or null if the path was invalid. + * + * @param path the path + * @param description the description + * @return the image icon + */ + protected static ImageIcon createImageIcon(String path, String description) { + java.net.URL imgURL = RoombaCommFrame.class.getResource(path); + if (imgURL != null) { + return new ImageIcon(imgURL, description); + } else { + System.err.println("Couldn't find file: " + path); + return null; + } + } + + // uh... we should try to avoid @SuppressWarnings ... + /** + * Play song. + * + * @param roombacomm the roombacomm + * @param rtttl the rtttl + */ + @SuppressWarnings("unchecked") + protected void playSong(RoombaComm roombacomm, String rtttl) { + ArrayList notelist = RTTTLParser.parse(rtttl); + int songsize = notelist.size(); + // if within the size of a roomba song, make the nsong, then play + if (songsize <= 16) { + updateDisplay("creating a song with createSong()", this.debug); + int notearray[] = new int[songsize * 2]; + int j = 0; + for (int i = 0; i < songsize; i++) { + Note note = (Note) notelist.get(i); + int sec64ths = note.duration * 64 / 1000; + notearray[j++] = note.notenum; + notearray[j++] = sec64ths; + } + roombacomm.createSong(1, notearray); + roombacomm.playSong(1); + } + // otherwise, try to play it in realtime + else { + updateDisplay("playing song in realtime with playNote()\n", + this.debug); + int fudge = 20; + for (int i = 0; i < songsize; i++) { + Note note = (Note) notelist.get(i); + int duration = note.duration; + int sec64ths = duration * 64 / 1000; + if (sec64ths < 5) + sec64ths = 5; + if (note.notenum != 0) + roombacomm.playNote(note.notenum, sec64ths); + roombacomm.pause(duration + fudge); + } + } + } + + /** + * Tribble start. + * + * @param roombacomm the roombacomm + * @param jtextarea the jtextarea + * @param tribbleOn the tribble on + */ + protected static void tribbleStart(RoombaComm roombacomm, + JTextArea jtextarea, boolean tribbleOn) { + createTribblePurrSong(roombacomm); + + updateDisplay(roombacomm, jtextarea, "Press return to exit.", roombacomm.debug); + + while (tribbleOn) { + purr(roombacomm, jtextarea); + if (Math.random() < 0.1) + bark(roombacomm, jtextarea); + roombacomm.pause(1500 + (int) (Math.random() * 500)); + // tribbleOn = keyIsPressed(); + roombacomm.updateSensors(); + boolean b = roombacomm.maxButton(); + updateDisplay(roombacomm, jtextarea, "max button is (" + b + ")",roombacomm.debug); + tribbleOn = (!b); + } + } + + /** + * Purr. + * + * @param roombacomm the roombacomm + * @param jtextarea the jtextarea + */ + protected static void purr(RoombaComm roombacomm, JTextArea jtextarea) { + updateDisplay(roombacomm, jtextarea, "purr", roombacomm.debug); + roombacomm.playSong(5); + for (int i = 0; i < 5; i++) { + roombacomm.spinLeftAt(75); + roombacomm.pause(100); + roombacomm.spinRightAt(75); + roombacomm.pause(100); + roombacomm.stop(); + } + } + + /** + * Creates the tribble purr song. + * + * @param roombacomm the roombacomm + */ + protected static void createTribblePurrSong(RoombaComm roombacomm) { + int song[] = { 68, 4, 67, 4, 66, 4, 65, 4, 64, 4, 63, 4, 62, 4, 61, 4, + 60, 4, 59, 4, 60, 4, 61, 4, }; + roombacomm.createSong(5, song); + } + + /** + * Bark. + * + * @param roombacomm the roombacomm + * @param jtextarea the jtextarea + */ + protected static void bark(RoombaComm roombacomm, JTextArea jtextarea) { + updateDisplay(roombacomm, jtextarea, "bark", roombacomm.debug); + roombacomm.vacuum(true); + roombacomm.playNote(50, 5); + roombacomm.pause(150); + roombacomm.vacuum(false); + } + + /** + * Gets the power_color. + * + * @return the power_color + */ + protected int getPower_color() { + return power_color; + } + + /** + * Sets the power_color. + * + * @param power_color the new power_color + */ + protected void setPower_color(int power_color) { + if (power_color >= 0 && power_color <= 255) { + this.power_color = power_color; + } else { + this.power_color = 0; + updateDisplay("invalid power color attempted (" + power_color + ")"); + } + } + + /** + * Gets the power_int. + * + * @return the power_int + */ + protected int getPower_int() { + return power_int; + } + + /** + * Sets the power_int. + * + * @param power_int the new power_int + */ + protected void setPower_int(int power_int) { + + if (power_color >= 0 && power_color <= 255) { + this.power_int = power_int; + } else { + this.power_int = 0; + updateDisplay("invalid power intensity attempted (" + power_color + + ")"); + } + } + + /** + * Sets the lE ds. + * + * @param roombacomm the new lE ds + */ + protected void setLEDs(RoombaComm roombacomm) { + if (!roombacomm.connected()) + return; + if (roombacomm.getProtocol().equalsIgnoreCase("SCI")) { + roombacomm.setLEDs(this.greenOn, this.redOn, this.toggleSpot, + this.toggleClean, this.toggleMax, this.toggleDirt, + this.power_color, this.power_int); + } + if (roombacomm.getProtocol().equalsIgnoreCase("OI")) { + roombacomm.setLEDsOI(this.toggleCheckRobot, this.toggleSpot, + this.toggleDock, this.toggleDirt, this.power_color, + this.power_int); + } + return; + } + + /** + * Sets the chg green led. + * + * @param roombacomm the roombacomm + * @param green the green + */ + protected void setChgGreenLED(RoombaComm roombacomm, boolean green) { + this.greenOn = green; + updateDisplay("setChgGreenLED", this.debug); + this.setLEDs(roombacomm); + } + + /** + * Sets the chg red led. + * + * @param roombacomm the roombacomm + * @param red the red + */ + protected void setChgRedLED(RoombaComm roombacomm, boolean red) { + this.redOn = red; + updateDisplay("setChgRedLED", this.debug); + this.setLEDs(roombacomm); + } + + /** + * Sets the chg spot led. + * + * @param roombacomm the roombacomm + * @param spot the spot + */ + protected void setChgSpotLED(RoombaComm roombacomm, boolean spot) { + updateDisplay("setChgSpotLED value(" + spot + ")", this.debug); + roombacomm.setChgSpotLED(roombacomm, spot); + this.toggleSpot = roombacomm.isToggleSpot(); + } + + /** + * Sets the chg clean led. + * + * @param roombacomm the roombacomm + * @param clean the clean + */ + protected void setChgCleanLED(RoombaComm roombacomm, boolean clean) { + updateDisplay("setChgCleanLED value(" + clean + ")", this.debug); + roombacomm.setChgCleanLED(roombacomm, clean); + this.toggleClean = roombacomm.isToggleClean(); + } + + /** + * Sets the chg max led. + * + * @param roombacomm the roombacomm + * @param max the max + */ + protected void setChgMaxLED(RoombaComm roombacomm, boolean max) { + updateDisplay("setChgMaxLED value(" + max + ")", this.debug); + roombacomm.setChgMaxLED(roombacomm, max); + this.toggleMax = roombacomm.isToggleMax(); + } + + /** + * Sets the chg dirt led. + * + * @param roombacomm the roombacomm + * @param dirt the dirt + */ + protected void setChgDirtLED(RoombaComm roombacomm, boolean dirt) { + updateDisplay("setChgDirtLED value(" + dirt + ")", this.debug); + roombacomm.setChgDirtLED(roombacomm, dirt); + this.toggleDirt = roombacomm.isToggleDirt(); + } + + /** + * Sets the toggle check robot. + * + * @param roombacomm the roombacomm + * @param CheckRobot the check robot + */ + public void setToggleCheckRobot(RoombaComm roombacomm, boolean CheckRobot) { + updateDisplay("setChgCheckRobotLED value(" + CheckRobot + ")",this.debug); + roombacomm.setChgCheckRobotLED(roombacomm, CheckRobot); + this.toggleCheckRobot = roombacomm.isToggleCheckRobot(); + } + + /** + * Sets the toggle dock. + * + * @param roombacomm the roombacomm + * @param dock the dock + */ + public void setToggleDock(RoombaComm roombacomm, boolean dock) { + updateDisplay("setChgDockLED value(" + dock + ")", this.debug); + roombacomm.setChgDockLED(roombacomm, dock); + this.toggleDock = roombacomm.isToggleDock(); + } + + /** + * Sets the chg power color led. + * + * @param roombacomm the roombacomm + * @param power_color the power_color + */ + protected void setChgPowerColorLED(RoombaComm roombacomm, int power_color) { + updateDisplay("setChgPowerColorLED value(" + power_color + ")",this.debug); + roombacomm.setChgPowerColorLED(roombacomm, power_color); + // this.power_color + // TODO: Keep track of power color + } + + /** + * Sets the chg power intensity led. + * + * @param roombacomm the roombacomm + * @param power_intensity the power_intensity + */ + protected void setChgPowerIntensityLED(RoombaComm roombacomm, int power_intensity) { + updateDisplay("setChgPowerIntensityLED value(" + power_intensity + ")",this.debug); + roombacomm.setChgPowerIntensityLED(roombacomm, power_intensity); + // TODO: Keep track of Power Intensity + } + + /** + * Gets the toggle check robot. + * + * @return the toggle check robot + */ + public boolean getToggleCheckRobot() { + return toggleCheckRobot; + } + + /** + * Gets the toggle dock. + * + * @return the toggle dock + */ + public boolean getToggleDock() { + return toggleDock; + } + + /** + * Gets the j tabbed panel config. + * + * @return the j tabbed panel config + */ + private JTabbedPane getJTabbedPanelConfig() { + if(jTabbedPanelConfig == null) { + jTabbedPanelConfig = new JTabbedPane(); + jTabbedPanelConfig.setPreferredSize(new java.awt.Dimension(139, 28)); + jTabbedPanelConfig.addTab("Net", null, getJPanelConfigNet(), "set the TCP network settings here"); + jTabbedPanelConfig.addTab("Serial", null, getJPanelConfigSerial(), "set the serial settings here"); + } + jTabbedPanelConfig.setMinimumSize(new Dimension(200,100)); + jTabbedPanelConfig.setPreferredSize(new java.awt.Dimension(254, 100)); + jTabbedPanelConfig.setToolTipText("Use one of the two connection methods to communicate with the Roomba"); + // Register a change listener + jTabbedPanelConfig.addChangeListener(new ChangeListener() { + // This method is called whenever the selected tab changes + public void stateChanged(ChangeEvent evt) { + debugPrintln(debug,"jTabbedPanelConfig - state changed"); + JTabbedPane pane = (JTabbedPane)evt.getSource(); + // Get current tab + int sel = pane.getSelectedIndex(); + debugPrintln(debug,"jTabbedPanelConfig - tab "+sel+" selected"); + debugPrintln(debug,"jTabbedPanelConfig - roombaCommSerial.isConnected() "+roombaCommSerial.isConnected()); + debugPrintln(debug,"jTabbedPanelConfig - roombaCommTCPClient.isConnected() "+roombaCommTCPClient.isConnected()); + if (roombaCommTCPClient.isConnected() && roombaCommSerial.isConnected()){ + debugPrintln(debug,"***** I have no idea why both should ever be connected at the same time... THIS IS STRANGE *****"); + } + if (sel == 1){ + setCommPorts(); + if (roombaCommTCPClient.isConnected() && !roombaCommSerial.isConnected()){ + pane.setSelectedIndex(0); + debugPrintln(debug,"active net connection found setting focus to the correct tab"); + } + } + if (sel == 0){ + if (!roombaCommTCPClient.isConnected() && roombaCommSerial.isConnected()){ + pane.setSelectedIndex(1); + debugPrintln(debug,"active Serial connection found setting focus to the correct tab"); + } + } + } + }); + return jTabbedPanelConfig; + } + + /** + * Gets the j panel config net. + * + * @return the j panel config net + */ + private JPanel getJPanelConfigNet() { + if(jPanelConfigNet == null) { + jPanelConfigNet = new JPanel(); + GridBagLayout jPanelConfigNetLayout = new GridBagLayout(); + jPanelConfigNetLayout.rowWeights = new double[] {0.0, 0.1}; + jPanelConfigNetLayout.rowHeights = new int[] {26, 7}; + jPanelConfigNetLayout.columnWeights = new double[] {0.1, 0.1, 0.1}; + jPanelConfigNetLayout.columnWidths = new int[] {7, 7, 7}; + jPanelConfigNet.setLayout(jPanelConfigNetLayout); + jPanelConfigNet.setPreferredSize(new java.awt.Dimension(318, 72)); + jPanelConfigNet.add(getJLabelHost(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.VERTICAL, new Insets(0, 0, 0, 0), 0, 0)); + jPanelConfigNet.add(getJTextFieldHost(), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + jPanelConfigNet.add(getJLabelPort(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jPanelConfigNet.add(getJTextFieldPort(), new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + { + netButton = new JButton(); + jPanelConfigNet.add(netButton, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + netButton.setText("connect"); + netButton.setActionCommand("net"); + netButton.addActionListener(this); + } + } + return jPanelConfigNet; + } + + /** + * Gets the j panel config serial. + * + * @return the j panel config serial + */ + private JPanel getJPanelConfigSerial() { + if(jPanelConfigSerial == null) { + jPanelConfigSerial = new JPanel(); + GridBagLayout jPanelConfigSerialLayout = new GridBagLayout(); + jPanelConfigSerial.setPreferredSize(new java.awt.Dimension(318, 72)); + jPanelConfigSerialLayout.rowWeights = new double[] {0.1}; + jPanelConfigSerialLayout.rowHeights = new int[] {7}; + jPanelConfigSerialLayout.columnWeights = new double[] {0.0, 0.1, 0.1}; + jPanelConfigSerialLayout.columnWidths = new int[] {77, 7, 7}; + jPanelConfigSerial.setLayout(jPanelConfigSerialLayout); + jPanelConfigSerial.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent evt) { + jPanelConfigSerialFocusGained(evt); + } + }); + { + portChoices = new JComboBox(); + jPanelConfigSerial.add(portChoices, new GridBagConstraints(1, -1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + portChoices.validate(); + portChoices.repaint(); + portChoices.addActionListener(this); + } + { + connectButton = new JButton(); + jPanelConfigSerial.add(connectButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jPanelConfigSerial.add(getJLabelCOMM(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0)); + connectButton.setText(" connect "); + connectButton.setActionCommand("connect"); + connectButton.addActionListener(this); + } + } + return jPanelConfigSerial; + } + + /** + * Gets the j label host. + * + * @return the j label host + */ + private JLabel getJLabelHost() { + if(jLabelHost == null) { + jLabelHost = new JLabel(); + jLabelHost.setLayout(null); + jLabelHost.setText("Host"); + jLabelHost.setPreferredSize(new java.awt.Dimension(39, 17)); + jLabelHost.setLabelFor(getJTextFieldHost()); + jLabelHost.setToolTipText("Set the hostname/IP address"); + } + return jLabelHost; + } + + /** + * Gets the j text field host. + * + * @return the j text field host + */ + private JTextField getJTextFieldHost() { + if(jTextFieldHost == null) { + jTextFieldHost = new JTextField(); + jTextFieldHost.setText("192.168.2.15"); + jTextFieldHost.setToolTipText("Set the hostname/IP address"); + } + return jTextFieldHost; + } + + /** + * Gets the j panel1. + * + * @return the j panel1 + */ + private JPanel getJPanel1() { + if(jPanel1 == null) { + jPanel1 = new JPanel(); + GridBagLayout jPanel1Layout = new GridBagLayout(); + jPanel1Layout.rowWeights = new double[] {0.1}; + jPanel1Layout.rowHeights = new int[] {7}; + jPanel1Layout.columnWeights = new double[] {0.1, 0.1}; + jPanel1Layout.columnWidths = new int[] {7, 7}; + jPanel1.setLayout(jPanel1Layout); + jPanel1.setPreferredSize(new java.awt.Dimension(800,320)); + { + ledPanel = new JPanel(); + GridBagLayout ledPanelLayout = new GridBagLayout(); + ledPanel.setLayout(ledPanelLayout); + jPanel1.add(ledPanel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + { + ledPanelSCIOnly = new JPanel(new GridLayout(3, 3)); + GridBagLayout ledPanel1Layout = new GridBagLayout(); + ledPanel.add(getLedPanelShared(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + ledPanel.add(getJPanel4x(), new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + ledPanel.add(ledPanelSCIOnly, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + ledPanel1Layout.rowWeights = new double[] {0.1}; + ledPanel1Layout.rowHeights = new int[] {7}; + ledPanel1Layout.columnWeights = new double[] {0.1, 0.1, 0.1, 0.1}; + ledPanel1Layout.columnWidths = new int[] {7, 7, 7, 7}; + ledPanelSCIOnly.setLayout(ledPanel1Layout); + ButtonGroup group = new ButtonGroup(); + String off="None"; + String green="Green"; + String red="Red"; + String both="Orange"; + { + JRadioButton statusOffButton = new JRadioButton(off); + ledPanelSCIOnly.add(statusOffButton, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + statusOffButton.setMnemonic(KeyEvent.VK_N); + statusOffButton.setActionCommand("StatusLED-" + off); + statusOffButton.setSelected(true); + group.add(statusOffButton); + statusOffButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateDisplay("setting Power Color = " + e.getActionCommand() + + "\n"); + setChgGreenLED(roombaCommSerial, false); + setChgRedLED(roombaCommSerial, false); + } + }); + } + { + JRadioButton statusGreenButton = new JRadioButton(green); + ledPanelSCIOnly.add(statusGreenButton, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + statusGreenButton.setMnemonic(KeyEvent.VK_G); + statusGreenButton.setActionCommand("StatusLED-" + green); + group.add(statusGreenButton); + statusGreenButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateDisplay("setting Power Color = " + e.getActionCommand() + + "\n"); + setChgGreenLED(roombaCommSerial, true); + setChgRedLED(roombaCommSerial, false); + } + }); + } + { + JRadioButton statusRedButton = new JRadioButton(red); + ledPanelSCIOnly.add(statusRedButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + statusRedButton.setMnemonic(KeyEvent.VK_R); + statusRedButton.setActionCommand("StatusLED-" + red); + group.add(statusRedButton); + statusRedButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateDisplay("setting Power Color = " + e.getActionCommand() + + "\n"); + setChgGreenLED(roombaCommSerial, false); + setChgRedLED(roombaCommSerial, true); + } + }); + } + { + JRadioButton statusBothButton = new JRadioButton(both); + ledPanelSCIOnly.add(statusBothButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + statusBothButton.setMnemonic(KeyEvent.VK_O); + statusBothButton.setActionCommand("StatusLED-" + both); + group.add(statusBothButton); + statusBothButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateDisplay("setting Power Color = " + e.getActionCommand() + + "\n"); + setChgGreenLED(roombaCommSerial, true); + setChgRedLED(roombaCommSerial, true); + } + }); + } + } + { + powerColorSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, 100); + ledPanel.add(powerColorSlider, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2)); + powerColorSlider.setPaintTicks(true); + powerColorSlider.setMajorTickSpacing(100); + powerColorSlider.setMinorTickSpacing(25); + powerColorSlider.setPaintLabels(true); + powerColorSlider.setSize(200, 46); + powerColorSlider.setPreferredSize(new java.awt.Dimension(224, 48)); + powerColorSlider.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + JSlider src = (JSlider) e.getSource(); + if (!src.getValueIsAdjusting()) { + setPower_color((int) src.getValue()); + updateDisplay("setting Power Color = " + getPower_color() + + "\n"); + setLEDs(roombaCommSerial); + } + } + }); + } + { + JLabel powerColorLabel = new JLabel("PowerColor", JLabel.CENTER); + ledPanel.add(powerColorLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 2, 2)); + } + { + powerColorIntensity = new JSlider(JSlider.HORIZONTAL, 0, 255, 100); + ledPanel.add(powerColorIntensity, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 2, 2)); + powerColorIntensity.setPaintTicks(true); + powerColorIntensity.setMajorTickSpacing(100); + powerColorIntensity.setMinorTickSpacing(25); + powerColorIntensity.setPaintLabels(true); + powerColorIntensity.setPreferredSize(new java.awt.Dimension(248, 45)); + powerColorIntensity.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + // System.err.println("stateChanged:"+e); + JSlider src = (JSlider) e.getSource(); + if (!src.getValueIsAdjusting()) { + setPower_int((int) src.getValue()); + updateDisplay("setting Power Color Intensity = " + + getPower_int() + "\n"); + setLEDs(roombaCommSerial); + } + } + }); + } + { + JLabel powerColorIntensityLabel = new JLabel("PowerColorIntensity", + JLabel.CENTER); + ledPanel.add(powerColorIntensityLabel, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 2, 2)); + } + ledPanelLayout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}; + ledPanelLayout.rowHeights = new int[] {7, 7, 7, 7, 7, 7, 7}; + ledPanelLayout.columnWeights = new double[] {0.1}; + ledPanelLayout.columnWidths = new int[] {7}; + ledPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("LEDs"),BorderFactory.createEmptyBorder(5,5,5,5))); + ledPanel.setPreferredSize(new java.awt.Dimension(288, 313)); + } + + { + ctrlPanel = new JPanel(); + jPanel1.add(ctrlPanel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + { + JPanel ctrlPanel1 = new JPanel(); + ctrlPanel.add(ctrlPanel1); + { + JButton but_turnleft = new JButton(); + ctrlPanel1.add(but_turnleft); + but_turnleft.setActionCommand("turnleft"); + but_turnleft.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_turnleft.png"))); + but_turnleft.addActionListener(this); + } + { + JButton but_forward = new JButton(); + ctrlPanel1.add(but_forward); + but_forward.setActionCommand("forward"); + but_forward.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_forward.png"))); + but_forward.addActionListener(this); + } + { + JButton but_turnright = new JButton(); + ctrlPanel1.add(but_turnright); + but_turnright.setActionCommand("turnright"); + but_turnright.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_turnright.png"))); + but_turnright.addActionListener(this); + } + { + JButton but_spinleft = new JButton(); + ctrlPanel1.add(but_spinleft); + but_spinleft.setActionCommand("spinleft"); + but_spinleft.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_spinleft.png"))); + but_spinleft.addActionListener(this); + } + { + JButton but_stop = new JButton(); + ctrlPanel1.add(but_stop); + but_stop.setActionCommand("stop"); + but_stop.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_stop.png"))); + but_stop.addActionListener(this); + } + { + JButton but_spinright = new JButton(); + ctrlPanel1.add(but_spinright); + but_spinright.setActionCommand("spinright"); + but_spinright.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_spinright.png"))); + but_spinright.addActionListener(this); + } + { + ctrlPanel1.add(new JLabel()); + } + { + JButton but_backward = new JButton(); + ctrlPanel1.add(but_backward); + but_backward.setActionCommand("backward"); + but_backward.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_backward.png"))); + but_backward.setPreferredSize(new java.awt.Dimension(55, 78)); + but_backward.addActionListener(this); + } + { + ctrlPanel1.add(new JLabel()); + } + ctrlPanel1.setLayout(new GridLayout(3, 3)); + ctrlPanel1.setPreferredSize(new java.awt.Dimension(194, 199)); + ctrlPanel1.setSize(194, 199); +// ctrlPanel1.setTabTitle(""); + } + { + speedSlider = new JSlider(JSlider.HORIZONTAL, 0, 500, 200); + ctrlPanel.add(speedSlider); + speedSlider.setPaintTicks(true); + speedSlider.setMajorTickSpacing(100); + speedSlider.setMinorTickSpacing(25); + speedSlider.setPaintLabels(true); + speedSlider.addChangeListener(this); + } + { + JLabel sliderLabel = new JLabel(); + ctrlPanel.add(sliderLabel); + sliderLabel.setText("speed (mm/s)"); + sliderLabel.setAlignmentX(JLabel.CENTER); + } + ctrlPanel.setLayout(new BoxLayout(ctrlPanel, BoxLayout.Y_AXIS)); + ctrlPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Movement"),BorderFactory.createEmptyBorder(5,5,5,5))); + } + } + return jPanel1; + } + + /** + * Gets the j label port. + * + * @return the j label port + */ + private JLabel getJLabelPort() { + if(jLabelPort == null) { + jLabelPort = new JLabel(); + jLabelPort.setText("Port"); + jLabelPort.setPreferredSize(new java.awt.Dimension(39, 33)); + jLabelPort.setLabelFor(getJTextFieldPort()); + jLabelPort.setToolTipText("Set the TCP port"); + } + return jLabelPort; + } + + /** + * Gets the j text field port. + * + * @return the j text field port + */ + private JTextField getJTextFieldPort() { + if(jTextFieldPort == null) { + jTextFieldPort = new JTextField(); + jTextFieldPort.setToolTipText("Set the TCP port"); + jTextFieldPort.setText("5001"); + } + return jTextFieldPort; + } + + /** + * J panel config serial focus gained. + * + * @param evt the evt + */ + private void jPanelConfigSerialFocusGained(FocusEvent evt) { + debugPrintln(debug,"jPanelConfigSerial.focusGained, event="+evt); + //TODO add your code for jPanelConfigSerial.focusGained + // make sure we have a roombaCommSerial object + setCommPorts(); + } + + /** + * Sets the comm ports. + */ + private void setCommPorts() { + debugPrintln(debug,"setCommPorts-start"); + if (roombaCommSerial == null){ + debugPrintln(debug,"setCommPorts-roombaCommSerial is null"); + roombaCommSerial = new RoombaCommSerial(this.debug); + } + if (portChoices != null) { + debugPrintln(debug,"setCommPorts-portChoices object is not null"); + // if the list of ports is empty then try to fill it + if (portChoices.getItemCount() ==0){ + // fill in the comm ports (combo box) with choices. + debugPrintln(debug,"setCommPorts-getting list of ports"); + String[] ports = roombaCommSerial.listPorts(); + // for now short cutting looking for serial ports to speed up start/stop of the UI + // String[] ports = {"a","b"}; + debugPrintln(debug,"setCommPorts-found "+ports.length+" serialports"); + for (int i = 0; i < ports.length; i++) { + String s = ports[i]; + debugPrintln(debug,"setCommPorts-adding ["+i+"] as "+s); + portChoices.addItem(ports[i]); + if (s.equals(roombaCommSerial.getPortname())) { + debugPrintln(debug,"setCommPorts- setting port as selected due to "+roombaCommSerial.getPortname()); + portChoices.setSelectedItem(s); + } + } + portChoices.validate(); + portChoices.repaint(); + } + } + debugPrintln(debug,"setCommPorts-start"); + } + + /** + * Gets the j label comm. + * + * @return the j label comm + */ + private JLabel getJLabelCOMM() { + if(jLabelCOMM == null) { + jLabelCOMM = new JLabel(); + jLabelCOMM.setText("COM"); + } + return jLabelCOMM; + } + + /** + * Gets the j panel2. + * + * @return the j panel2 + */ + private JPanel getJPanel2() { + if(jPanel2 == null) { + jPanel2 = new JPanel(); + GridBagLayout jPanel2Layout = new GridBagLayout(); + jPanel2Layout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1}; + jPanel2Layout.rowHeights = new int[] {7, 7, 7, 7, 7, 7}; + jPanel2Layout.columnWeights = new double[] {0.0}; + jPanel2Layout.columnWidths = new int[] {150}; + jPanel2.setLayout(jPanel2Layout); + jPanel2.setBorder(BorderFactory.createTitledBorder(null,"Commands",TitledBorder.LEADING,TitledBorder.DEFAULT_POSITION)); + jPanel2.setPreferredSize(new java.awt.Dimension(233,505)); + jPanel2.add(getJPanelModes(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + jPanel2.add(getJPanelSounds(), new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + jPanel2.add(getJPanelTestPrograms(), new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + jPanel2.add(getJPanelVacuum(), new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + jPanel2.add(getJPanelPower(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + jPanel2.add(getJPanel4(), new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + } + return jPanel2; + } + + /** + * Gets the j panel3. + * + * @return the j panel3 + */ + private JPanel getJPanel3() { + if(jPanel3 == null) { + jPanel3 = new JPanel(); + jPanel3.setVisible(false); + GridBagLayout jPanel3Layout = new GridBagLayout(); + jPanel3Layout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}; + jPanel3Layout.rowHeights = new int[] {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; + jPanel3Layout.columnWeights = new double[] {0.1}; + jPanel3Layout.columnWidths = new int[] {7}; + jPanel3.setLayout(jPanel3Layout); + { + JButton but_nyi = new JButton(); + jPanel3.add(but_nyi, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi.setText("nyi"); + but_nyi.setVisible(false); + } + { + JButton but_nyi2 = new JButton(); + jPanel3.add(but_nyi2, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi2.setText("nyi2"); + but_nyi2.setVisible(false); + } + { + JButton but_nyi3 = new JButton(); + jPanel3.add(but_nyi3, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi3.setText("nyi3"); + but_nyi3.setVisible(false); + } + { + JButton but_nyi4 = new JButton(); + jPanel3.add(but_nyi4, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi4.setText("nyi4"); + but_nyi4.setVisible(false); + } + { + JButton but_nyi5 = new JButton(); + jPanel3.add(but_nyi5, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi5.setText("nyi5"); + but_nyi5.setVisible(false); + } + { + JButton but_nyi6 = new JButton(); + jPanel3.add(but_nyi6, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi6.setText("nyi6"); + but_nyi6.setVisible(false); + } + { + JButton but_nyi7 = new JButton(); + jPanel3.add(but_nyi7, new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi7.setText("nyi7"); + but_nyi7.setVisible(false); + } + { + jButton2 = new JButton(); + jPanel3.add(jButton2, new GridBagConstraints(0, 9, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jButton2.setText("nyi5"); + jButton2.setVisible(false); + } + { + jButton1 = new JButton(); + jPanel3.add(jButton1, new GridBagConstraints(0, 10, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jButton1.setText("nyi6"); + jButton1.setVisible(false); + } + { + jButton3 = new JButton(); + jPanel3.add(jButton3, new GridBagConstraints(0, 11, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jButton3.setText("nyi3"); + jButton3.setVisible(false); + } + { + jButton4 = new JButton(); + jPanel3.add(jButton4, new GridBagConstraints(0, 12, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jButton4.setText("nyi2"); + jButton4.setVisible(false); + } + } + return jPanel3; + } + + /** + * Gets the j panel modes. + * + * @return the j panel modes + */ + private JPanel getJPanelModes() { + if(jPanelModes == null) { + jPanelModes = new JPanel(); + GridBagLayout jPanelModesLayout = new GridBagLayout(); + jPanelModesLayout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1}; + jPanelModesLayout.rowHeights = new int[] {7, 7, 7, 7, 7}; + jPanelModesLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelModesLayout.columnWidths = new int[] {95, 95}; + jPanelModes.setLayout(jPanelModesLayout); + jPanelModes.setBorder(BorderFactory.createTitledBorder("Modes")); + { + JButton but_spot = new JButton(); + jPanelModes.add(but_spot, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_spot.setText("spot"); + but_spot.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_spot.addActionListener(this); + } + { + JButton but_full = new JButton(); + jPanelModes.add(but_full, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_full.setText("full"); + but_full.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_full.addActionListener(this); + } + { + JButton but_safe = new JButton(); + jPanelModes.add(but_safe, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_safe.setText("safe"); + but_safe.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_safe.addActionListener(this); + } + { + JButton but_dock = new JButton(); + jPanelModes.add(but_dock, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_dock.setText("dock"); + but_dock.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_dock.addActionListener(this); + } + { + JButton but_max = new JButton(); + jPanelModes.add(but_max, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_max.setText("max"); + but_max.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_max.addActionListener(this); + } + { + JButton but_clean = new JButton(); + jPanelModes.add(but_clean, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_clean.setText("clean"); + but_clean.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_clean.addActionListener(this); + } + { + JButton but_wakeup = new JButton(); + jPanelModes.add(but_wakeup, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_wakeup.setText("wakeup"); + but_wakeup.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_wakeup.addActionListener(this); + } + { + JButton but_reset = new JButton(); + jPanelModes.add(but_reset, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_reset.setText("reset"); + but_reset.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_reset.addActionListener(this); + } + { + JButton but_passive = new JButton(); + jPanelModes.add(but_passive, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_passive.setText("passive"); + but_passive.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_passive.addActionListener(this); + } + } + return jPanelModes; + } + + /** + * Gets the j panel sounds. + * + * @return the j panel sounds + */ + private JPanel getJPanelSounds() { + if(jPanelSounds == null) { + jPanelSounds = new JPanel(); + GridBagLayout jPanelSoundsLayout = new GridBagLayout(); + jPanelSoundsLayout.rowWeights = new double[] {0.1, 0.1}; + jPanelSoundsLayout.rowHeights = new int[] {7, 7}; + jPanelSoundsLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelSoundsLayout.columnWidths = new int[] {95, 95}; + jPanelSounds.setLayout(jPanelSoundsLayout); + jPanelSounds.setBorder(BorderFactory.createTitledBorder("Sounds")); + { + JButton but_OSU = new JButton(); + jPanelSounds.add(but_OSU, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_OSU.setText("OSU"); + but_OSU.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_OSU.addActionListener(this); + } + { + JButton but_beeplo = new JButton(); + jPanelSounds.add(but_beeplo, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_beeplo.setText("beep-lo"); + but_beeplo.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_beeplo.addActionListener(this); + } + { + JButton but_beephi = new JButton(); + jPanelSounds.add(but_beephi, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_beephi.setText("beep-hi"); + but_beephi.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_beephi.addActionListener(this); + } + { + JButton but_playRTTL = getBut_playRTTL(); + jPanelSounds.add(getBut_playRTTL(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_playRTTL.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_playRTTL.addActionListener(this); + } + } + return jPanelSounds; + } + + /** + * Gets the j panel test programs. + * + * @return the j panel test programs + */ + private JPanel getJPanelTestPrograms() { + if(jPanelTestPrograms == null) { + jPanelTestPrograms = new JPanel(); + GridBagLayout jPanelTestProgramsLayout = new GridBagLayout(); + jPanelTestProgramsLayout.rowWeights = new double[] {0.1}; + jPanelTestProgramsLayout.rowHeights = new int[] {7}; + jPanelTestProgramsLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelTestProgramsLayout.columnWidths = new int[] {95, 95}; + jPanelTestPrograms.setLayout(jPanelTestProgramsLayout); + jPanelTestPrograms.setBorder(BorderFactory.createTitledBorder("Test Programs")); + { + JButton but_TribbleOn = new JButton(); + jPanelTestPrograms.add(but_TribbleOn, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_TribbleOn.setText("Tribble On"); + but_TribbleOn.setPreferredSize(new java.awt.Dimension(93, 26)); + but_TribbleOn.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_TribbleOn.setSize(93, 26); + but_TribbleOn.addActionListener(this); + } + { + JButton but_test = new JButton(); + jPanelTestPrograms.add(but_test, new GridBagConstraints(1, 0, 2, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_test.setText("LogoA.square"); + but_test.setPreferredSize(new java.awt.Dimension(93, 26)); + but_test.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_test.setSize(93, 26); + but_test.addActionListener(this); + } + } + return jPanelTestPrograms; + } + + /** + * Gets the j panel4. + * + * @return the j panel4 + */ + private JPanel getJPanel4() { + if(jPanelSensors == null) { + jPanelSensors = new JPanel(); + GridBagLayout jPanelSensorsLayout = new GridBagLayout(); + jPanelSensorsLayout.rowWeights = new double[] {0.1}; + jPanelSensorsLayout.rowHeights = new int[] {7}; + jPanelSensorsLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelSensorsLayout.columnWidths = new int[] {95, 95}; + jPanelSensors.setLayout(jPanelSensorsLayout); + jPanelSensors.setBorder(BorderFactory.createTitledBorder(null, "Sensors", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION)); + { + JButton but_chargedata = new JButton(); + jPanelSensors.add(but_chargedata, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0)); + but_chargedata.setText("chargedata"); + but_chargedata.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_chargedata.setPreferredSize(new java.awt.Dimension(93, 26)); + but_chargedata.setSize(93, 26); + but_chargedata.addActionListener(this); + } + { + JButton but_sensors = new JButton(); + jPanelSensors.add(but_sensors, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0)); + but_sensors.setText("sensors"); + but_sensors.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_sensors.setPreferredSize(new java.awt.Dimension(93, 26)); + but_sensors.setSize(93, 26); + but_sensors.addActionListener(this); + } + } + return jPanelSensors; + } + + /** + * Gets the j panel vacuum. + * + * @return the j panel vacuum + */ + private JPanel getJPanelVacuum() { + if(jPanelVacuum == null) { + jPanelVacuum = new JPanel(); + GridBagLayout jPanelVacuumLayout = new GridBagLayout(); + jPanelVacuumLayout.rowWeights = new double[] {0.1}; + jPanelVacuumLayout.rowHeights = new int[] {7}; + jPanelVacuumLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelVacuumLayout.columnWidths = new int[] {95, 95}; + jPanelVacuum.setLayout(jPanelVacuumLayout); + jPanelVacuum.setBorder(BorderFactory.createTitledBorder(BorderFactory.createTitledBorder(""), "Vacuum", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION)); + { + JButton but_vacon = new JButton(); + jPanelVacuum.add(but_vacon, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_vacon.setText("vacuum-on"); + but_vacon.setSize(93, 26); + but_vacon.setPreferredSize(new java.awt.Dimension(93, 26)); + but_vacon.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_vacon.addActionListener(this); + } + { + JButton but_vacoff = new JButton(); + but_vacoff.setLayout(null); + jPanelVacuum.add(but_vacoff, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_vacoff.setText("vacuum-off"); + but_vacoff.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_vacoff.setPreferredSize(new java.awt.Dimension(93, 26)); + but_vacoff.setSize(93, 26); + but_vacoff.addActionListener(this); + } + } + return jPanelVacuum; + } + + /** + * Gets the j panel power. + * + * @return the j panel power + */ + private JPanel getJPanelPower() { + if(jPanelPower == null) { + jPanelPower = new JPanel(); + GridBagLayout jPanelPowerLayout = new GridBagLayout(); + jPanelPower.setBorder(BorderFactory.createTitledBorder(null, "Power", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION)); + jPanelPowerLayout.rowWeights = new double[] {0.1}; + jPanelPowerLayout.rowHeights = new int[] {7}; + jPanelPowerLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelPowerLayout.columnWidths = new int[] {95, 95}; + jPanelPower.setLayout(jPanelPowerLayout); + { + JButton but_powerOn = new JButton(); + jPanelPower.add(but_powerOn, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_powerOn.setText("power-on"); + but_powerOn.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_powerOn.addActionListener(this); + } + { + JButton but_power = new JButton(); + jPanelPower.add(but_power, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_power.setText("power-off"); + but_power.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_power.addActionListener(this); + } + } + return jPanelPower; + } + + /** + * Gets the j text pane1. + * + * @return the j text pane1 + */ + private JTextPane getJTextPane1() { + if(jTextPane1 == null) { + jTextPane1 = new JTextPane(); + jTextPane1.setText("Set the Protocal, then connect via a network or Serial connection.\nThen you can use the Commands, LED's, and/or Movement controls to operate your Roomba."); + jTextPane1.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + jTextPane1.setOpaque(false); + } + return jTextPane1; + } + + /** + * Gets the j panel4x. + * + * @return the j panel4x + */ + private JPanel getJPanel4x() { + if(ledPanelOIOnly == null) { + ledPanelOIOnly = new JPanel(); + GridBagLayout jPanel4Layout = new GridBagLayout(); + ledPanelOIOnly.setPreferredSize(new java.awt.Dimension(134, 49)); + jPanel4Layout.rowWeights = new double[] {0.1}; + jPanel4Layout.rowHeights = new int[] {7}; + jPanel4Layout.columnWeights = new double[] {0.1, 0.1, 0.1, 0.1}; + jPanel4Layout.columnWidths = new int[] {7, 7, 7, 7}; + ledPanelOIOnly.setLayout(jPanel4Layout); + ledPanelOIOnly.setVisible(false); + { + JButton but_toggleDock = new JButton(); + GridBagLayout but_toggleDockLayout = new GridBagLayout(); + ledPanelOIOnly.add(but_toggleDock, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_toggleDock.setActionCommand("toggleDock"); + but_toggleDock.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_dockOn.png"))); + but_toggleDock.setPreferredSize(new java.awt.Dimension(40, 40)); + but_toggleDock.setSize(40, 40); + but_toggleDockLayout.rowWeights = new double[] {0.1}; + but_toggleDockLayout.rowHeights = new int[] {7}; + but_toggleDockLayout.columnWeights = new double[] {0.1, 0.1}; + but_toggleDockLayout.columnWidths = new int[] {7, 7}; + but_toggleDock.setLayout(but_toggleDockLayout); + but_toggleDock.addActionListener(this); + } + { + JButton but_toggleCheckRobot = new JButton(); + GridBagLayout but_toggleCheckRobotLayout = new GridBagLayout(); + ledPanelOIOnly.add(but_toggleCheckRobot, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_toggleCheckRobot.setActionCommand("toggleCheckRobot"); + but_toggleCheckRobot.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_checkrobotOn.png"))); + but_toggleCheckRobot.setPreferredSize(new java.awt.Dimension(40, 40)); + but_toggleCheckRobot.setSize(40, 40); + but_toggleCheckRobotLayout.rowWeights = new double[] {0.1}; + but_toggleCheckRobotLayout.rowHeights = new int[] {7}; + but_toggleCheckRobotLayout.columnWeights = new double[] {0.1, 0.1}; + but_toggleCheckRobotLayout.columnWidths = new int[] {7, 7}; + but_toggleCheckRobot.setLayout(but_toggleCheckRobotLayout); + but_toggleCheckRobot.addActionListener(this); + } + } + return ledPanelOIOnly; + } + + /** + * Gets the led panel shared. + * + * @return the led panel shared + */ + private JPanel getLedPanelShared() { + if(ledPanelShared == null) { + ledPanelShared = new JPanel(); + { + JButton but_toggleSpot = new JButton(); + ledPanelShared.add(but_toggleSpot); + but_toggleSpot.setActionCommand("toggleSpot"); + but_toggleSpot.setIcon(getIcon("com/hackingroomba/roombacomm/images/but_spotOn.png")); + but_toggleSpot.setPreferredSize(new java.awt.Dimension(40, 40)); + but_toggleSpot.setSize(40, 40); + but_toggleSpot.addActionListener(this); + } + { + JButton but_toggleClean = new JButton(); + ledPanelShared.add(but_toggleClean); + but_toggleClean.setActionCommand("toggleClean"); + but_toggleClean.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_cleanOn.png"))); + but_toggleClean.setPreferredSize(new java.awt.Dimension(40, 40)); + but_toggleClean.setSize(40, 40); + but_toggleClean.addActionListener(this); + } + { + JButton but_toggleDirt = new JButton(); + ledPanelShared.add(but_toggleDirt); + but_toggleDirt.setActionCommand("toggleDirt"); + but_toggleDirt.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_dirtOn.png"))); + but_toggleDirt.setPreferredSize(new java.awt.Dimension(40, 40)); + but_toggleDirt.setSize(40, 40); + but_toggleDirt.addActionListener(this); + } + { + JButton but_toggleMax = new JButton(); + ledPanelShared.add(but_toggleMax); + but_toggleMax.setActionCommand("toggleMax"); + but_toggleMax.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_maxOn.png"))); + but_toggleMax.setSize(40, 40); + but_toggleMax.setPreferredSize(new java.awt.Dimension(40, 40)); + but_toggleMax.addActionListener(this); + } + } + return ledPanelShared; + } + + /** + * Gets the icon. + * + * @param str the str + * @return the icon + * @return + */ + private ImageIcon getIcon(String str) { + java.net.URL file =getClass().getClassLoader().getResource(str); + if (file != null){ + return new ImageIcon(file); + }else{ + // return the default icon + //TODO: Get a better default icon. :) + System.err.println("failed to find icon (" + str + ")"); + return new ImageIcon("com/hackingroomba/roombacomm/images/but_spotOn.png"); + } + } + + /** + * Gets the j panel4xx. + * + * @return the j panel4xx + */ + private JPanel getJPanel4xx() { + if(jPanel4 == null) { + jPanel4 = new JPanel(); + GridBagLayout jPanel4Layout1 = new GridBagLayout(); + jPanel4.setLayout(jPanel4Layout1); + jPanel4.setPreferredSize(new java.awt.Dimension(806, 608)); + { + selectPanel = new JPanel(); + GridBagLayout selectPanelLayout = new GridBagLayout(); + selectPanel.setLayout(selectPanelLayout); + jPanel4.add(selectPanel, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + { + protocolChoices = new JComboBox(protocols); + selectPanel.add(protocolChoices, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 5, 0, 5), 0, 0)); + protocolChoices.setSelectedIndex(protocolChoices.getSelectedIndex() >=0 ? protocolChoices.getSelectedIndex() : 0); + protocolChoices.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0), "Protocal", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION)); + protocolChoices.setToolTipText("Set the protocal based on the roomba hardware version"); + protocolChoices.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + debugPrintln(debug,"protocolChoices.itemStateChanged, event="+evt); + //TODO add your code for protocolChoices.itemStateChanged + if (evt.getItem().equals(protocolChoices.getItemAt(0)) && (evt.getStateChange() == ItemEvent.SELECTED)){ + ledPanelOIOnly.setVisible(false); + ledPanelSCIOnly.setVisible(true); + } + if (evt.getItem().equals(protocolChoices.getItemAt(1)) && (evt.getStateChange() == ItemEvent.SELECTED)){ + ledPanelOIOnly.setVisible(true); + ledPanelSCIOnly.setVisible(false); + } + ledPanelOIOnly.repaint(); + ledPanelSCIOnly.repaint(); + } + }); + protocolChoices.addActionListener(this); + } + { + handshakeButton = new JCheckBox("h/w
handshake"); + selectPanel.add(handshakeButton, new GridBagConstraints(0, 2, 1, 1, 0.1, 0.1, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + selectPanel.add(getJTabbedPanelConfig(), new GridBagConstraints(1, 1, 1, 2, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + selectPanel.add(getJTextPane1(), new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 2, 2)); + handshakeButton.setPreferredSize(new java.awt.Dimension(148, 40)); + handshakeButton.setText("h/w handshake"); + handshakeButton.setToolTipText("check if you want to use the hardware handshake setting"); + } + selectPanelLayout.rowWeights = new double[] {0.0, 0.0}; + selectPanelLayout.rowHeights = new int[] {53, 58}; + selectPanelLayout.columnWeights = new double[] {0.0, 0.0}; + selectPanelLayout.columnWidths = new int[] {177, 375}; + selectPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Select Roomba Type & Port"),BorderFactory.createEmptyBorder(1,1,1,1))); + selectPanel.setPreferredSize(new java.awt.Dimension(535,128)); + } + { + displayPanel = new JPanel(); + jPanel4.add(displayPanel, new GridBagConstraints(1, 2, 3, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + jPanel4.add(getJPanel1(), new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jPanel4.add(getJPanel2(), new GridBagConstraints(2, 0, 1, 2, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jPanel4.add(getJPanel3(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + GridBagLayout displayPanelLayout = new GridBagLayout(); + displayPanelLayout.rowWeights = new double[] {0.1}; + displayPanelLayout.rowHeights = new int[] {7}; + displayPanelLayout.columnWeights = new double[] {0.1}; + displayPanelLayout.columnWidths = new int[] {7}; + displayPanel.setLayout(displayPanelLayout); + displayPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Display"),BorderFactory.createEmptyBorder(1,1,1,1))); + displayPanel.setPreferredSize(new java.awt.Dimension(803, 67)); + { + displayText = new JTextArea(10, 75); + displayText.setEditable(false); + JScrollPane scrollPane = new JScrollPane(displayText, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + displayPanel.add(scrollPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + scrollPane.setSize(795, 163); + scrollPane.setPreferredSize(new java.awt.Dimension(791, 38)); + displayText.setLineWrap(false); + } + } + jPanel4Layout1.rowWeights = new double[] {0.1, 0.1, 2.0}; + jPanel4Layout1.rowHeights = new int[] {7, 7, 80}; + jPanel4Layout1.columnWeights = new double[] {0.1, 0.1, 0.1}; + jPanel4Layout1.columnWidths = new int[] {7, 7, 7}; + } + return jPanel4; + } + + /** + * Debug println. + * + * @param debug the debug + * @param str the str + */ + private void debugPrintln(boolean debug, String str){ + if (debug){ + System.out.println(str); + } + } + + /** + * This window closed. + * + * @param evt the evt + */ + private void thisWindowClosed(WindowEvent evt) { + System.out.println("this.windowClosed, event="+evt); + //TODO: add code for this.windowClosed ? Do we need anything else? + dispose(); + } + + /** + * Gets the but_play rttl. + * + * @return the but_play rttl + */ + private JButton getBut_playRTTL() { + if(but_playRTTL == null) { + but_playRTTL = new JButton(); + but_playRTTL.setText("Play RTTL"); + but_playRTTL.setVisible(false); + } + return but_playRTTL; + } +} \ No newline at end of file diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommGUI.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommGUI.java new file mode 100644 index 0000000..4d588c3 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommGUI.java @@ -0,0 +1,125 @@ +/* + * RoombaCommGUI -- GUI to test out RoombaComm + * + * 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 com.hackingroomba.roombacomm; + +/** + * A simple wrapper for RoombaCommFrame. + * + * This class shows someone how to use RoombaCommFrame from the "outside". + * AKA: No Java Swing skills needed for this class. (is the goal) + * + * SVN id value is $Id: RoombaCommGUI.java 136 2010-04-06 00:07:23Z black.123 $ + */ +import jargs.examples.gnu.AutoHelpParser; +//TODO: CMB: add support for different initial GUI sizes (640(w)x480(H),1024x768... maybe others) +import jargs.gnu.CmdLineParser; + +/** + * This is an example class that will demonstrate how to use RoombaCommFrame.java.
+ * It also demonstrates a use of jargs to support command line parsing for this class. + * + */ +public class RoombaCommGUI { + + /** The hwhandshake. */ + boolean hwhandshake = false; + + /** The debug boolean will increase STDOUT to show details about the process at runtime. */ + boolean debug = false; + + /** + * The main method simply instantiates an instance of this class and passes in any command line arguments to that class.
+ * See RoombaCommGUI(java.lang.String[] args) for details about Command Line Interface (CLI) values + * + * @param args the arguments + */ + public static void main(String[] args) { + new RoombaCommGUI(args); + } + + /** + * Instantiates a new RoombaComm GUI without any args. + */ + public RoombaCommGUI() { + this(new String[0]); + } + + /** + * Instantiates a new RoombaComm GUI with any args by use of jargs. + * + * @param args the args
+ * --hwhandshake boolean value (true,false)
+ * -d,--debug boolean value to increase STDOUT
+ * -h,--help print usage input and exit
+ */ + public RoombaCommGUI(String[] args) { + AutoHelpParser parser = new AutoHelpParser(); + CmdLineParser.Option hwhandshake_opt = parser.addHelp(parser.addBooleanOption("hwhandshake"),"Use this to turn on the hardware hand shake"); + CmdLineParser.Option debug_opt = parser.addHelp(parser.addBooleanOption('d', "debug"),"output more information at runtime"); + CmdLineParser.Option help_opt = parser.addHelp(parser.addBooleanOption('h', "help"),"Show this help message"); + if (args != null){ + String[] org_args = args; + try { + parser.parse(args); + } + catch ( CmdLineParser.OptionException e ) { + System.err.println(e.getMessage()); + parser.printUsage(); + System.exit(2); + } + if ( Boolean.TRUE.equals(parser.getOptionValue(hwhandshake_opt))) { + hwhandshake = true; + } + if ( Boolean.TRUE.equals(parser.getOptionValue(debug_opt))) { + debug = true; + } + if (debug){ + for( int i=0; i < org_args.length; i++ ) { + System.out.println(i+": "+org_args[i]); + } + } + if ( Boolean.TRUE.equals(parser.getOptionValue(help_opt))) { + parser.printUsage(); + System.exit(0); + } + } + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + public void run() { + createAndShowGUI(debug); + } + }); + } + + /** + * Create the GUI and show it. For thread safety, + * this method should be invoked from the event dispatch thread. + * + * @param debug to increase STDOUT at runtime + */ + private static void createAndShowGUI(boolean debug) { + RoombaCommFrame rcPanel = new RoombaCommFrame(debug); + rcPanel.setResizable(true); + rcPanel.pack(); + rcPanel.setVisible(true); + } +} \ No newline at end of file diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommPanel.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommPanel.java new file mode 100644 index 0000000..9ccdd0e --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommPanel.java @@ -0,0 +1,1854 @@ +/* + * RoombaCommPanel - + * + * 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 com.hackingroomba.roombacomm; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.JTabbedPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.JTextPane; +import javax.swing.border.TitledBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.text.DefaultCaret; + +/** + * A Panel containing controls for testing RoombaComm. Normally put inside of a + * frame, for example see RoombaCommTest + * + * SVN id value is $Id: RoombaCommPanel.java 136 2010-04-06 00:07:23Z black.123 $ + */ +public class RoombaCommPanel extends JPanel implements ActionListener, + ChangeListener { + + private static final long serialVersionUID = 1L; + + { + // Set Look & Feel + try { + javax.swing.UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + JPanel ctrlPanel, selectPanel, buttonPanel, displayPanel, ledPanel; + private JTextPane jTextPane1; + private JPanel jPanelPower; + private JPanel jPanelVacuum; + private JPanel jPanelSensors; + private JPanel jPanelTestPrograms; + JComboBox portChoices; + JComboBox protocolChoices; + JCheckBox handshakeButton; + JTextArea displayText; + JButton connectButton; + JButton netButton; + JSlider speedSlider, powerColorSlider, powerColorIntensity; + private boolean debug = false; + boolean tribbleOn = false; + // default values for flags for LEDs + boolean redOn = false; + boolean greenOn = false; + boolean toggleSpot = false; + boolean toggleClean = false; + boolean toggleMax = false; + boolean toggleDirt = false; + boolean toggleCheckRobot = false; + boolean toggleDock = false; + private JPanel jPanelSounds; + private JPanel jPanelModes; + private JPanel jPanel3; + private JPanel jPanel2; + private JLabel jLabelCOMM; + private JTextField jTextFieldPort; + private JLabel jLabelPort; + private JPanel jPanel1; + private JTextField jTextFieldHost; + private JLabel jLabelHost; + private JPanel jPanelConfigSerial; + private JPanel jPanelConfigNet; + private JTabbedPane jTabbedPanelConfig; + private JButton jButton4; + private JButton jButton3; + private JButton jButton2; + private JButton jButton1; + int power_color = 0; + int power_int = 0; + RoombaCommSerial roombaCommSerial; + RoombaComm roombaComm; + RoombaCommTCPClient roombaCommTCPClient; + SimpleDateFormat formatter = new SimpleDateFormat( + "EEE, dd-MMM-yyyy HH:mm:ss"); + public RoombaCommPanel() { + this(false); + } + + public RoombaCommPanel(boolean debug) { +// super(new BorderLayout()); + super(); +// this.setContentPane(contentPane) + System.out.println("RoombaCommPanel-start"); + System.out.println(" debug is ("+debug+")"); + initialize(); + roombaCommSerial = new RoombaCommSerial(); + roombaCommTCPClient = new RoombaCommTCPClient(); + this.debug = debug; + roombaCommSerial.debug = debug; + roombaCommTCPClient.debug = debug; + System.out.println("RoombaCommPanel-makePanels-start"); + makePanels(); + System.out.println("RoombaCommPanel-makePanels-end"); + System.out.println("RoombaCommPanel-end"); + } + + /** + * This method initializes this + * + */ + private void initialize() { + Dimension defaultSize = new Dimension(800,600); + this.addComponentListener(new java.awt.event.ComponentListener() { + public void componentResized(java.awt.event.ComponentEvent e) { + System.out.println("147-componentResized("+e.getComponent().getWidth()+","+e.getComponent().getHeight()+")"); // TODO Auto-generated Event stub componentResized() + } + public void componentMoved(java.awt.event.ComponentEvent e) { + } + public void componentShown(java.awt.event.ComponentEvent e) { + } + public void componentHidden(java.awt.event.ComponentEvent e) { + } + }); + System.out.println("initialize : setting size to (" +defaultSize.getWidth()+","+defaultSize.getHeight()+")"); + GridBagLayout thisLayout = new GridBagLayout(); + this.setSize(defaultSize); + thisLayout.rowWeights = new double[] {0.0, 0.0, 0.1}; + thisLayout.rowHeights = new int[] {137, 304, 5}; + thisLayout.columnWeights = new double[] {0.0, 0.0}; + thisLayout.columnWidths = new int[] {538, 206}; + this.setLayout(thisLayout); + this.setPreferredSize(new java.awt.Dimension(800, 600)); //638 +// { +// buttonPanel = new JPanel(); +// this.add(buttonPanel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); +// GridBagLayout buttonPanelLayout = new GridBagLayout(); +// buttonPanelLayout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}; +// buttonPanelLayout.rowHeights = new int[] {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; +// buttonPanelLayout.columnWeights = new double[] {0.0, 0.0}; +// buttonPanelLayout.columnWidths = new int[] {10, 10}; +// buttonPanel.setLayout(buttonPanelLayout); +// buttonPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Commands"),BorderFactory.createEmptyBorder(5,5,5,5))); +// buttonPanel.setMinimumSize(new java.awt.Dimension(0,10)); +// buttonPanel.setSize(200, 411); +// buttonPanel.setPreferredSize(new java.awt.Dimension(242, 249)); +// } + } + + /** + * 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() { + System.out.println("connect-start"); + String portname = (String) portChoices.getSelectedItem(); + // roombacomm.debug=true; + roombaCommSerial.setWaitForDSR(handshakeButton.isSelected()); + int i = protocolChoices.getSelectedIndex(); + roombaCommSerial.setProtocol((i == 0) ? "SCI" : "OI"); + if (portname == null){ + // I guess we do not yet have ports so refresh the list + setCommPorts(); + } + updateDisplay("connecting to " + portname + "\n"); + connectButton.setText("connecting"); + if (portname != null){ + if (!roombaCommSerial.connect(portname)) { + updateDisplay("Couldn't connect to " + portname + "\n"); + connectButton.setText(" connect "); + // roombacomm.debug=false; + System.out.println("connect-end (could not connect)"); + return false; + }else{ + updateDisplay("connected to " + portname + "\n"); + } + }else{ + updateDisplay("you must first select a COMM port to use before you can attempt to connect"); + jTabbedPanelConfig.setSelectedIndex(1); + return false; + } + updateDisplay("Roomba startup\n"); + + roombaCommSerial.startup(); + roombaCommSerial.control(); + roombaCommSerial.playNote(72, 10); // C , test note + roombaCommSerial.pause(200); + + connectButton.setText("disconnect"); + connectButton.setActionCommand("disconnect"); + roombaComm = roombaCommSerial; + // roombacomm.debug=true; + updateDisplay("Checking for Roomba... "); + if (roombaCommSerial.updateSensors()) { + updateDisplay("Roomba found!\n"); + System.out.println("connect-end"); + return true; + }else{ + updateDisplay("No Roomba. :( Is it turned on?\n"); + System.out.println("connect-end"); + return true ; + } + } + + public boolean connectNet(String portname) { + System.out.println("connectNet called"); + int i = protocolChoices.getSelectedIndex(); + if (roombaCommTCPClient == null){ + roombaCommTCPClient = new RoombaCommTCPClient(); + roombaCommTCPClient.setConnected(false); + } + roombaCommTCPClient.setProtocol((i == 0) ? "SCI" : "OI"); + + if (!roombaCommTCPClient.connect(portname)) { + updateDisplay("Couldn't connect to " + portname); + return false; + } + + updateDisplay("Roomba startup on port " + portname); + roombaCommTCPClient.startup(); + roombaCommTCPClient.control(); + // roombacomm.setSensorsAutoUpdate(true); + roombaCommTCPClient.pause(30); + + updateDisplay("Checking for Roomba... \n"); + // roombaCommTCPClient.setDebug(true); + if (roombaCommTCPClient.updateSensors()) { + updateDisplay("Roomba found!\n"); + updateDisplay(roombaCommTCPClient.getSensorsAsString()); + } else { + updateDisplay("No Roomba. :( Is it turned on?\n"); + } + updateDisplay("buffer is(" + roombaCommTCPClient.getBuffer() + ")", + this.debug); + // roombaCommTCPClient.setDebug(false); + // roombacomm.updateSensors(); + updateDisplay("connected (" + roombaCommTCPClient.connected() + ")\n"); + if (roombaCommTCPClient.connected()) { + updateDisplay("Playing some notes\n"); + roombaCommTCPClient.playNote(72, 10); // C + roombaCommTCPClient.pause(200); + roombaCommTCPClient.playNote(79, 10); // G + roombaCommTCPClient.pause(200); + roombaCommTCPClient.playNote(76, 10); // E + roombaCommTCPClient.pause(200); + netButton.setText("disconnect-net"); + netButton.setActionCommand("disconnect-net"); + roombaComm = roombaCommTCPClient; // set the pointer so that the + // action stuff can work against + // any class + } + + // roombaCommSerial.setWaitForDSR(handshakeButton.isSelected()); + // + // + // connectButton.setText("connecting"); + // if( ! roombaCommSerial.connect( portname ) ) { + // updateDisplay("Couldn't connect to "+portname+"\n"); + // connectButton.setText(" connect "); + // //roombacomm.debug=false; + // return false; + // } + // updateDisplay("Roomba startup\n"); + // + // roombaCommSerial.startup(); + // roombaCommSerial.control(); + // roombaCommSerial.playNote( 72, 10 ); // C , test note + // roombaCommSerial.pause( 200 ); + // + // connectButton.setText("disconnect"); + // connectButton.setActionCommand("disconnect"); + // //roombacomm.debug=true; + // updateDisplay("Checking for Roomba... "); + // if( roombaCommSerial.updateSensors() ) + // updateDisplay("Roomba found!\n"); + // else + // updateDisplay("No Roomba. :( Is it turned on?\n"); + + return true; + } + + /** */ + public void disconnect() { + roombaCommSerial.disconnect(); + connectButton.setText(" connect "); + connectButton.setActionCommand("connect"); + } + + public void disconnectNet() { + roombaCommTCPClient.disconnect(); + netButton.setText(" net "); + netButton.setActionCommand("net"); + } + + /** + * Play a (MIDI) note, that is, make the Roomba a musical instrument + * notenums 32-127: notenum == corresponding note played thru beeper + * velocity == duration in number of 1/64s of a second (e.g. 64==1second) + * notenum 24: notenum == main vacuum velocity == non-zero turns on, zero + * turns off notenum 25: blink LEDs, velcoity is color of Power notenum 28 & + * 29: spin left & spin right, velocity is speed + * + */ + public void playMidiNote(int notenum, int velocity) { + updateDisplay("play note: " + notenum + "," + velocity + "\n"); + if (!roombaCommSerial.connected()) + return; + + if (notenum >= 31) { // G and above + if (velocity == 0) + return; + if (velocity < 4) + velocity = 4; // has problems at lower durations + else + velocity = velocity / 2; + roombaCommSerial.playNote(notenum, velocity); + } else if (notenum == 24) { // C + roombaCommSerial.vacuum(!(velocity == 0)); + } else if (notenum == 25) { // C# + boolean lon = (velocity != 0); + int inten = (lon) ? 255 : 128; // either full bright or half bright + roombaCommSerial.setLEDs(lon, lon, lon, lon, lon, lon, + velocity * 2, inten); + } else if (notenum == 28) { // E + if (velocity != 0) + roombaCommSerial.spinLeftAt(velocity * 2); + else + roombaCommSerial.stop(); + } else if (notenum == 29) { // F + if (velocity != 0) + roombaCommSerial.spinRightAt(velocity * 2); + else + roombaCommSerial.stop(); + } + } + + /** implement actionlistener */ + public void actionPerformed(ActionEvent event) { + String action = event.getActionCommand(); + updateDisplay(formatter.format(new Date()) + ": action (" + action + + ") happened\n", this.debug); + // roombacomm.setLEDs(false, false, false, false, false, false, 0, 0); + if ("comboBoxChanged".equals(action)) { +// String portname = (String) portChoices.getSelectedItem(); + int i = protocolChoices.getSelectedIndex(); + if (roombaComm != null) { + roombaComm.setProtocol((i == 0) ? "SCI" : "OI"); + } else { + updateDisplay( + formatter.format(new Date()) + + ": null roombaComm object found in actionPerformed\n", + this.debug); + } + return; + } + // updateDisplay(action+"\n"); + if ("net".equals(action)) { + if (jTextFieldHost != null && jTextFieldHost.getText() != null && jTextFieldPort != null && jTextFieldPort.getText() != null){ + if(connectNet(jTextFieldHost.getText()+":"+jTextFieldPort.getText())){ + // TODO: find a way to hide/disable the other connect tab until the session is disconnected +// getJTabbedPanelConfig().getComponentAt(1).setVisible(false); // hide the Serial tab while we are connected with a Net port + }else{ + // TODO: reenable/show the other tab(s) +// getJTabbedPanelConfig().getComponentAt(0).setVisible(true); // make sure the net tab is not hidden when we fail to connect + } + + }else{ + updateDisplay("connect (via net) pressed with missing values:"); + if (jTextFieldHost != null && jTextFieldHost.getText() != null){ + updateDisplay(" host :"+jTextFieldHost.getText()); + }else{ + updateDisplay(" host : MISSING VALUE"); + } + if (jTextFieldPort != null && jTextFieldPort.getText() != null){ + updateDisplay(" port :"+jTextFieldPort.getText()); + }else{ + updateDisplay(" port : MISSING VALUE"); + } + } + return; + } else if ("disconnect-net".equals(action)) { + disconnectNet(); + // TODO: reenable/show the other tab(s) +// getJTabbedPanelConfig().getComponentAt(1).setVisible(true); // show the serial tab while we disconnect from the net + return; + } + if ("connect".equals(action)) { + if (connect()){ + // TODO: find a way to hide/disable the other connect tab until the session is disconnected + }else{ + // TODO: reenable/show the other tab(s) +// getJTabbedPanelConfig().getComponentAt(0).setEnabled(true); // make sure the net tab is not hidden when we fail to connect +// getJTabbedPanelConfig().repaint(100); + } + return; + } else if ("disconnect".equals(action)) { + disconnect(); + // TODO: reenable/show the other tab(s) +// getJTabbedPanelConfig().getComponentAt(0).setEnabled(true); // show the net tab while we disconnect from the serial + return; + } + // stop right here if we're not connected + if (roombaComm == null || !roombaComm.connected()) { + updateDisplay("not connected!\n"); + return; + } + + if ("stop".equals(action)) { + roombaComm.stop(); + } else if ("forward".equals(action)) { + // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n"); + roombaComm.goForward(); + } else if ("backward".equals(action)) { + // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n"); + roombaComm.goBackward(); + } else if ("spinleft".equals(action)) { + // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n"); + roombaComm.spinLeft(); + } else if ("spinright".equals(action)) { + // updateDisplay("Speed is("+roombaComm.getSpeed()+")\n"); + roombaComm.spinRight(); + } else if ("turnleft".equals(action)) { + roombaComm.turnLeft(); + } else if ("turnright".equals(action)) { + roombaComm.turnRight(); + } else if ("max".equals(action)) { + roombaComm.max(); + } else if ("dock".equals(action)) { + roombaComm.dock(); + } else if ("test".equals(action)) { + LogoA.square(roombaComm, 300); + /* + * 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 ("OSU".equals(action)) { + updateDisplay("Going to play OSU\n"); + + roombaComm.stop(); + roombaComm.pause(500); + // RTTTLPlay rp = new RTTTLPlay(); + // /dev/cu.KeySerial1 + // 'tron:d=4,o=5,b=200:8f6,8c6,8g,e,8p,8f6,8c6,8g,8f6,8c6,8g,e,8p,8f6,8c6,8g,e.,2d' + // String[] + // osuSong={roombacomm.getPortname(),"OSU:d=4,o=5,b=125:a,g,a#,a,8g#,a,8g#,2a,8p,8f,8g,8g#,a,8g#,a,8g#,a,g,f,a,g,8a,g,d,8f,8p,8f,8p,8f,8p,8f,8p,c6,a,g,f,8a#,a,8g,f,p,c6,a,g,a,8a#,a,8a#,c6,p,2d6,d,8c6,a#,g,f,8f,8f,8g,a#,8g,a#,a,2a#"}; + playSong( + roombaComm, + "OSU:d=4,o=5,b=125:a,g,a#,a,8g#,a,8g#,2a,8p,8f,8g,8g#,a,8g#,a,8g#,a,g,f,a,g,8a,g,d,8f,8p,8f,8p,8f,8p,8f,8p,c6,a,g,f,8a#,a,8g,f,p,c6,a,g,a,8a#,a,8a#,c6,p,2d6,d,8c6,a#,g,f,8f,8f,8g,a#,8g,a#,a,2a#"); + + // playSong(roombacomm, + // "Baa Baa Black Sheep:d=4,o=5,b=125:c,c,g,g,8a,8b,8c6,8a,g,p,f,f,e,e,d,d,c"); + roombaComm.stop(); + } else if ("Tribble On".equals(action)) { + tribbleOn = true; + tribbleStart(roombaComm, displayText, tribbleOn); + } else if ("reset".equals(action)) { + roombaComm.stop(); + roombaComm.startup(); + roombaComm.control(); + } else if ("passive".equals(action)) { + //passive / start command + roombaComm.start(); + } else if ("safe".equals(action)) { + roombaComm.safe(); + } else if ("full".equals(action)) { + roombaComm.full(); + } else if ("power-off".equals(action)) { + roombaComm.powerOff(); + } else if ("power-on".equals(action)) { + roombaComm.powerOn(); + } else if ("wakeup".equals(action)) { + roombaComm.wakeup(); + } else if ("beep-lo".equals(action)) { + roombaComm.playNote(50, 32); // C1 + roombaComm.pause(200); + } else if ("beep-hi".equals(action)) { + roombaComm.playNote(90, 32); // 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"); + } else if ("chargedata".equals(action)) { + if (roombaComm.updateSensors()) + updateDisplay("*****\n" + roombaComm.chargeDataAsString() + + "\n"); + else + updateDisplay("couldn't read Roomba. Is it connected?\n"); + } else if ("toggleGreen".equals(action)) { + setChgGreenLED(roombaComm, !greenOn); + } else if ("toggleRed".equals(action)) { + setChgRedLED(roombaComm, !redOn); + } else if ("toggleSpot".equals(action)) { + setChgSpotLED(roombaComm, !toggleSpot); + } else if ("toggleClean".equals(action)) { + setChgCleanLED(roombaComm, !toggleClean); + } else if ("toggleMax".equals(action)) { + setChgMaxLED(roombaComm, !toggleMax); + } else if ("toggleDirt".equals(action)) { + setChgDirtLED(roombaComm, !toggleDirt); + } else if ("toggleCheckRobot".equals(action)) { + setToggleCheckRobot(roombaComm, !toggleCheckRobot); + } else if ("toggleDock".equals(action)) { + setToggleDock(roombaComm, !toggleDock); + } + + } + + /** implement ChangeListener, for the slider */ + public void stateChanged(ChangeEvent e) { + // System.err.println("stateChanged:"+e); + JSlider src = (JSlider) e.getSource(); + if (!src.getValueIsAdjusting()) { + int speed = (int) src.getValue(); + speed = (speed < 1) ? 1 : speed; // don't allow zero speed + updateDisplay("setting speed = " + speed + "\n"); + roombaComm.setSpeed(speed); + } + } + + /** + * + */ + void makePanels() { + System.out.println("makePanels-start"); + makeSelectPanel(); + this.add(selectPanel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2)); + makeCtrlPanel(); + makeLedPanel(); + makeButtonPanel(); + makeDisplayPanel(); + this.add(displayPanel, new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2)); + this.add(getJPanel1(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 2, 2)); + this.add(getJPanel2(), new GridBagConstraints(1, 0, 1, 2, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2)); + this.add(getJPanel3(), new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + + // pack(); //setVisible(true); + updateDisplay("RoombaComm, version " + RoombaComm.VERSION + "\n"); + System.out.println("makePanels-finish"); + } + void makeButtonPanel(){ +// jPanel1.add(buttonPanel, new GridBagConstraints(3, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + } + void makeSelectPanel() { + selectPanel = new JPanel(); + GridBagLayout selectPanelLayout = new GridBagLayout(); + selectPanelLayout.rowWeights = new double[] {0.0, 0.0}; + selectPanelLayout.rowHeights = new int[] {53, 58}; + selectPanelLayout.columnWeights = new double[] {0.0, 0.0}; + selectPanelLayout.columnWidths = new int[] {181, 88}; + selectPanel.setLayout(selectPanelLayout); + + // Create a combo box with protocols + String[] protocols = { "Roomba 1xx-4xx (SCI)", "Roomba 5xx (OI)" }; + protocolChoices = new JComboBox(protocols); + String p = roombaCommSerial.getProtocol(); + protocolChoices.setSelectedIndex(p.equals("SCI") ? 0 : 1); + +// // Create a combo box with choices. +// // String[] ports = roombaCommSerial.listPorts(); +// // for now short cutting looking for serial ports to speed up start/stop of the UI +// String[] ports = {"a","b"}; +// System.out.println("found "+ports.length+" serialports"); +// // portChoices = new JComboBox(ports); + portChoices = new JComboBox(); +// for (int i = 0; i < ports.length; i++) { +// String s = ports[i]; +// System.out.println("adding ["+i+"] as "+s); +// portChoices.addItem(ports[i]); +// if (s.equals(roombaCommSerial.getPortname())) { +// System.out.println(" setting port as selected due to "+roombaCommSerial.getPortname()); +// portChoices.setSelectedItem(s); +// } +// } + + handshakeButton = new JCheckBox("h/w
handshake"); + // net button + + // Add a border around the select panel. + selectPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory + .createTitledBorder("Select Roomba Type & Port"), BorderFactory + .createEmptyBorder(1, 1, 1, 1))); + System.out.println("577-setPreferredSize"); + selectPanel.setPreferredSize(new java.awt.Dimension(535, 128)); + System.out.println("577-setPreferredSize-done"); + selectPanel.add(protocolChoices, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 5, 0, 5), 0, 0)); + protocolChoices.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0), "Protocal", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION)); + protocolChoices.setToolTipText("Set the protocal based on the roomba hardware version"); +// selectPanel.setJMenuBar(getJMenuBar1()); + selectPanel.add(handshakeButton, new GridBagConstraints(0, 2, 1, 1, 0.1, 0.1, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + handshakeButton.setPreferredSize(new java.awt.Dimension(148, 40)); + handshakeButton.setText("h/w handshake"); + handshakeButton.setToolTipText("check if you want to use the hardware handshake setting"); + selectPanel.add(getJTabbedPanelConfig(), new GridBagConstraints(1, 1, 1, 2, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + selectPanel.add(getJTextPane1(), new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 2, 2)); + + // Listen to events from the combo box. + protocolChoices.addActionListener(this); + } + + /** + * + */ + void makeCtrlPanel() { + + } + + void makeLedPanel() { + + // if (roombaCommSerial.getProtocol().equalsIgnoreCase("SCI")) { +// String off = "None"; +// String green = "Green"; +// String red = "Red"; +// String both = "Orange"; + + // Group the radio buttons. +// ButtonGroup group = new ButtonGroup(); + // } + + // powerColorIntensity.addChangeListener(this); + // powerColorIntensity.addChangeListener(new ChangeListener() {setPow + // }); + + } // End make led panel + + /** + * + */ + void makeDisplayPanel() { + displayPanel = new JPanel(); + GridBagLayout displayPanelLayout = new GridBagLayout(); + displayPanelLayout.rowWeights = new double[] {0.1}; + displayPanelLayout.rowHeights = new int[] {7}; + displayPanelLayout.columnWeights = new double[] {0.1}; + displayPanelLayout.columnWidths = new int[] {7}; + displayPanel.setLayout(displayPanelLayout); + displayPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory + .createTitledBorder("Display"), BorderFactory + .createEmptyBorder(1, 1, 1, 1))); + displayPanel.setPreferredSize(new java.awt.Dimension(412, 45)); + { + displayText = new JTextArea(10, 75); + displayPanel.add(displayText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + displayText.setLineWrap(false); + DefaultCaret caret = (DefaultCaret)displayText.getCaret(); + caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); + displayText.setCaret(caret); + displayText.setPreferredSize(new java.awt.Dimension(825, 27)); + displayText.setSize(795, 160); + + } + + JTextArea displayText2 = new JTextArea(25, 30); + displayText2.setLineWrap(true); + DefaultCaret dc = new DefaultCaret(); + dc.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); + // only works on Java 1.5+ + // dc.setUpdatePolicy( DefaultCaret.ALWAYS_UPDATE ); + JScrollPane scrollPane = new JScrollPane(displayText, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.getVerticalScrollBar(); + // scrollPane.add(displayText2,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + // JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + displayPanel.add(scrollPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + scrollPane.setSize(795, 163); + scrollPane.setPreferredSize(new java.awt.Dimension(766, 81)); + } + + protected void updateDisplay(String s) { + System.out.println("updateDisplay(string): start"); + displayText.append(s); + if (s != null && !(s.endsWith("\n"))) { + displayText.append("\n"); + } +// displayText.setCaretPosition(displayText.getDocument().getLength()); + System.out.println("updateDisplay(string): reposition to "+displayText.getDocument().getLength()); + // TODO: need to find a way to auto scroll to the end of the text so the user can just follow along. + // currently on my PC. I am not even able to scroll down to the end of the text. sigh... + displayText.getParent().validate(); + displayText.getParent().repaint(100); + displayText.setText(displayText.getText() ); + +//// for (int i = 0; i < 20; i++) { +//// displayText.append("This is text " + i + "\n"); +// scrollPaneVbar.setValue(scrollPaneVbar.getMaximum()); +// scrollPaneVbar.paint(scrollPaneVbar.getGraphics()); +// displayText.scrollRectToVisible(displayText.getVisibleRect()); +// if (displayText != null && displayText.getGraphics() != null){ +// displayText.paint(displayText.getGraphics()); +// scrollPaneVbar.getParent().getParent().repaint(100); +//// displayText.repaint(100); +// System.out.println("displayText calling paint on displayText "); // this should never happen +// }else{ +// if (displayText != null){ +// System.out.println("displayText is null"); // this should never happen +// } +// if (displayText.getGraphics() != null){ +// System.out.println("displayText.getGraphics is null"); // this might happen? +// } +// } +// try { +// Thread.sleep(250); +// } catch (InterruptedException ex) { +//// Logger.getLogger(ScrollTextView.class.getName()).log(Level.SEVERE, null, ex); +// System.out.println("InterruptedException while trying to update displayText"); +// } +//// } +// // displayText.getParent().validate(); + System.out.println("updateDisplay(string): end"); + } + + protected void updateDisplay(String s, boolean onlyDebug) { + if (onlyDebug && (roombaCommSerial.debug || roombaCommTCPClient.debug)) { + updateDisplay(s); + System.out.println(s); + } + } + + protected static void updateDisplay(RoombaComm roombacomm, + JTextArea jtextarea, String s, boolean onlyDebug) { + if (onlyDebug && roombacomm.debug) { + jtextarea.append(s); + jtextarea.setCaretPosition(jtextarea.getDocument().getLength()); + jtextarea.getParent().validate(); + jtextarea.getParent().repaint(100); + System.out.println(s); + } + } + + /** 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; + } + } + + @SuppressWarnings("unchecked") + protected void playSong(RoombaComm roombacomm, String rtttl) { + ArrayList notelist = RTTTLParser.parse(rtttl); + int songsize = notelist.size(); + // if within the size of a roomba song, make the nsong, then play + if (songsize <= 16) { + updateDisplay("creating a song with createSong()", this.debug); + int notearray[] = new int[songsize * 2]; + int j = 0; + for (int i = 0; i < songsize; i++) { + Note note = (Note) notelist.get(i); + int sec64ths = note.duration * 64 / 1000; + notearray[j++] = note.notenum; + notearray[j++] = sec64ths; + } + roombacomm.createSong(1, notearray); + roombacomm.playSong(1); + } + // otherwise, try to play it in realtime + else { + updateDisplay("playing song in realtime with playNote()\n", + this.debug); + int fudge = 20; + for (int i = 0; i < songsize; i++) { + Note note = (Note) notelist.get(i); + int duration = note.duration; + int sec64ths = duration * 64 / 1000; + if (sec64ths < 5) + sec64ths = 5; + if (note.notenum != 0) + roombacomm.playNote(note.notenum, sec64ths); + roombacomm.pause(duration + fudge); + } + } + } + + protected static void tribbleStart(RoombaComm roombacomm, + JTextArea jtextarea, boolean tribbleOn) { + createTribblePurrSong(roombacomm); + + updateDisplay(roombacomm, jtextarea, "Press return to exit.", + roombacomm.debug); + + while (tribbleOn) { + + purr(roombacomm, jtextarea); + + if (Math.random() < 0.1) + bark(roombacomm, jtextarea); + + roombacomm.pause(1500 + (int) (Math.random() * 500)); + // tribbleOn = keyIsPressed(); + roombacomm.updateSensors(); + boolean b = roombacomm.maxButton(); + updateDisplay(roombacomm, jtextarea, "max button is (" + b + ")", + roombacomm.debug); + tribbleOn = (!b); + } + } + + protected static void purr(RoombaComm roombacomm, JTextArea jtextarea) { + updateDisplay(roombacomm, jtextarea, "purr", roombacomm.debug); + roombacomm.playSong(5); + for (int i = 0; i < 5; i++) { + roombacomm.spinLeftAt(75); + roombacomm.pause(100); + roombacomm.spinRightAt(75); + roombacomm.pause(100); + roombacomm.stop(); + } + } + + protected static void createTribblePurrSong(RoombaComm roombacomm) { + int song[] = { 68, 4, 67, 4, 66, 4, 65, 4, 64, 4, 63, 4, 62, 4, 61, 4, + 60, 4, 59, 4, 60, 4, 61, 4, }; + roombacomm.createSong(5, song); + } + + protected static void bark(RoombaComm roombacomm, JTextArea jtextarea) { + updateDisplay(roombacomm, jtextarea, "bark", roombacomm.debug); + roombacomm.vacuum(true); + roombacomm.playNote(50, 5); + roombacomm.pause(150); + roombacomm.vacuum(false); + } + + protected int getPower_color() { + return power_color; + } + + protected void setPower_color(int power_color) { + if (power_color >= 0 && power_color <= 255) { + this.power_color = power_color; + } else { + this.power_color = 0; + updateDisplay("invalid power color attempted (" + power_color + ")"); + } + } + + protected int getPower_int() { + return power_int; + } + + protected void setPower_int(int power_int) { + + if (power_color >= 0 && power_color <= 255) { + this.power_int = power_int; + } else { + this.power_int = 0; + updateDisplay("invalid power intensity attempted (" + power_color + + ")"); + } + } + + protected void setLEDs(RoombaComm roombacomm) { + if (!roombacomm.connected()) + return; + if (roombacomm.getProtocol().equalsIgnoreCase("SCI")) { + roombacomm.setLEDs(this.greenOn, this.redOn, this.toggleSpot, + this.toggleClean, this.toggleMax, this.toggleDirt, + this.power_color, this.power_int); + } + if (roombacomm.getProtocol().equalsIgnoreCase("OI")) { + roombacomm.setLEDsOI(this.toggleCheckRobot, this.toggleSpot, + this.toggleDock, this.toggleDirt, this.power_color, + this.power_int); + } + return; + } + + protected void setChgGreenLED(RoombaComm roombacomm, boolean green) { + this.greenOn = green; + updateDisplay("setChgGreenLED", this.debug); + this.setLEDs(roombacomm); + } + + protected void setChgRedLED(RoombaComm roombacomm, boolean red) { + this.redOn = red; + updateDisplay("setChgRedLED", this.debug); + this.setLEDs(roombacomm); + } + + protected void setChgSpotLED(RoombaComm roombacomm, boolean spot) { + updateDisplay("setChgSpotLED value(" + spot + ")", this.debug); + roombacomm.setChgSpotLED(roombacomm, spot); + this.toggleSpot = roombacomm.isToggleSpot(); + } + + protected void setChgCleanLED(RoombaComm roombacomm, boolean clean) { + updateDisplay("setChgCleanLED value(" + clean + ")", this.debug); + roombacomm.setChgCleanLED(roombacomm, clean); + this.toggleClean = roombacomm.isToggleClean(); + } + + protected void setChgMaxLED(RoombaComm roombacomm, boolean max) { + // updateDisplay("setChgMaxLED",this.debug); + // this.toggleMax=max; + // this.setLEDs(roombacomm); + updateDisplay("setChgMaxLED value(" + max + ")", this.debug); + roombacomm.setChgMaxLED(roombacomm, max); + this.toggleMax = roombacomm.isToggleMax(); + } + + protected void setChgDirtLED(RoombaComm roombacomm, boolean dirt) { + updateDisplay("setChgDirtLED value(" + dirt + ")", this.debug); + roombacomm.setChgDirtLED(roombacomm, dirt); + this.toggleDirt = roombacomm.isToggleDirt(); + } + + public void setToggleCheckRobot(RoombaComm roombacomm, boolean CheckRobot) { + updateDisplay("setChgCheckRobotLED value(" + CheckRobot + ")", + this.debug); + roombacomm.setChgCheckRobotLED(roombacomm, CheckRobot); + this.toggleCheckRobot = roombacomm.isToggleCheckRobot(); + } + + public void setToggleDock(RoombaComm roombacomm, boolean dock) { + updateDisplay("setChgDockLED value(" + dock + ")", this.debug); + roombacomm.setChgDockLED(roombacomm, dock); + this.toggleDock = roombacomm.isToggleDock(); + } + + protected void setChgPowerColorLED(RoombaComm roombacomm, int power_color) { + updateDisplay("setChgPowerColorLED value(" + power_color + ")", + this.debug); + // this.power_color=power_color; + roombacomm.setChgPowerColorLED(roombacomm, power_color); + // this.power_color + // TODO: Keep track of power color + } + + protected void setChgPowerIntensityLED(RoombaComm roombacomm, + int power_intensity) { + updateDisplay("setChgPowerIntensityLED value(" + power_intensity + ")", + this.debug); + roombacomm.setChgPowerIntensityLED(roombacomm, power_intensity); + // TODO: Keep track of Power Intensity + } + + public boolean getToggleCheckRobot() { + return toggleCheckRobot; + } + + public boolean getToggleDock() { + return toggleDock; + } + +// private JPanel getJPanelMiddle() { +// if(jPanelMiddle == null) { +// jPanelMiddle = new JPanel(); +// } +// return jPanelMiddle; +// } + + private JTabbedPane getJTabbedPanelConfig() { + if(jTabbedPanelConfig == null) { + jTabbedPanelConfig = new JTabbedPane(); + jTabbedPanelConfig.setPreferredSize(new java.awt.Dimension(139, 28)); + jTabbedPanelConfig.addTab("Net", null, getJPanelConfigNet(), "set the TCP network settings here"); + jTabbedPanelConfig.addTab("Serial", null, getJPanelConfigSerial(), "set the serial settings here"); + } + jTabbedPanelConfig.setMinimumSize(new Dimension(200,100)); + jTabbedPanelConfig.setPreferredSize(new java.awt.Dimension(323, 100)); + jTabbedPanelConfig.setToolTipText("Use one of the two connection methods to communicate with the Roomba"); + // Register a change listener + jTabbedPanelConfig.addChangeListener(new ChangeListener() { + // This method is called whenever the selected tab changes + public void stateChanged(ChangeEvent evt) { + System.out.println("jTabbedPanelConfig - state changed"); + JTabbedPane pane = (JTabbedPane)evt.getSource(); + // Get current tab + int sel = pane.getSelectedIndex(); + System.out.println("jTabbedPanelConfig - tab "+sel+" selected"); + System.out.println("jTabbedPanelConfig - roombaCommSerial.isConnected() "+roombaCommSerial.isConnected()); + System.out.println("jTabbedPanelConfig - roombaCommTCPClient.isConnected() "+roombaCommTCPClient.isConnected()); + if (roombaCommTCPClient.isConnected() && roombaCommSerial.isConnected()){ + System.out.println("***** I have no idea why both should ever be connected at the same time... THIS IS STRANGE *****"); + } + if (sel == 1){ + setCommPorts(); + if (roombaCommTCPClient.isConnected() && !roombaCommSerial.isConnected()){ + pane.setSelectedIndex(0); + System.out.println("active net connection found setting focus to the correct tab"); + } + } + if (sel == 0){ + if (!roombaCommTCPClient.isConnected() && roombaCommSerial.isConnected()){ + pane.setSelectedIndex(1); + System.out.println("active Serial connection found setting focus to the correct tab"); + } + } + } + }); + + return jTabbedPanelConfig; + } + + private JPanel getJPanelConfigNet() { + if(jPanelConfigNet == null) { + jPanelConfigNet = new JPanel(); + GridBagLayout jPanelConfigNetLayout = new GridBagLayout(); + jPanelConfigNetLayout.rowWeights = new double[] {0.0, 0.1}; + jPanelConfigNetLayout.rowHeights = new int[] {26, 7}; + jPanelConfigNetLayout.columnWeights = new double[] {0.1, 0.1, 0.1}; + jPanelConfigNetLayout.columnWidths = new int[] {7, 7, 7}; + jPanelConfigNet.setLayout(jPanelConfigNetLayout); + jPanelConfigNet.setPreferredSize(new java.awt.Dimension(318, 72)); + jPanelConfigNet.add(getJLabelHost(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.VERTICAL, new Insets(0, 0, 0, 0), 0, 0)); + jPanelConfigNet.add(getJTextFieldHost(), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + jPanelConfigNet.add(getJLabelPort(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jPanelConfigNet.add(getJTextFieldPort(), new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + { + netButton = new JButton(); + jPanelConfigNet.add(netButton, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + netButton.setText("connect"); + netButton.setActionCommand("net"); + netButton.addActionListener(this); + } + } + return jPanelConfigNet; + } + + private JPanel getJPanelConfigSerial() { + if(jPanelConfigSerial == null) { + jPanelConfigSerial = new JPanel(); + GridBagLayout jPanelConfigSerialLayout = new GridBagLayout(); + jPanelConfigSerial.setPreferredSize(new java.awt.Dimension(318, 72)); + jPanelConfigSerialLayout.rowWeights = new double[] {0.1}; + jPanelConfigSerialLayout.rowHeights = new int[] {7}; + jPanelConfigSerialLayout.columnWeights = new double[] {0.1, 0.1, 0.1}; + jPanelConfigSerialLayout.columnWidths = new int[] {7, 7, 7}; + jPanelConfigSerial.setLayout(jPanelConfigSerialLayout); + jPanelConfigSerial.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent evt) { + jPanelConfigSerialFocusGained(evt); + } + }); + { + portChoices = new JComboBox(); + jPanelConfigSerial.add(portChoices, new GridBagConstraints(1, -1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + portChoices.validate(); + portChoices.repaint(); + portChoices.addActionListener(this); + } + { + connectButton = new JButton(); + jPanelConfigSerial.add(connectButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jPanelConfigSerial.add(getJLabelCOMM(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0)); + connectButton.setText(" connect "); + connectButton.setActionCommand("connect"); + connectButton.addActionListener(this); + } + } + return jPanelConfigSerial; + } + + private JLabel getJLabelHost() { + if(jLabelHost == null) { + jLabelHost = new JLabel(); + jLabelHost.setLayout(null); + jLabelHost.setText("Host"); + jLabelHost.setPreferredSize(new java.awt.Dimension(39, 17)); + jLabelHost.setLabelFor(getJTextFieldHost()); + jLabelHost.setToolTipText("Set the hostname/IP address"); + } + return jLabelHost; + } + + private JTextField getJTextFieldHost() { + if(jTextFieldHost == null) { + jTextFieldHost = new JTextField(); + jTextFieldHost.setText("192.168.15.240"); + jTextFieldHost.setToolTipText("Set the hostname/IP address"); + } + return jTextFieldHost; + } + + private JPanel getJPanel1() { + if(jPanel1 == null) { + jPanel1 = new JPanel(); + GridBagLayout jPanel1Layout = new GridBagLayout(); + jPanel1Layout.rowWeights = new double[] {0.1}; + jPanel1Layout.rowHeights = new int[] {7}; + jPanel1Layout.columnWeights = new double[] {0.1, 0.1}; + jPanel1Layout.columnWidths = new int[] {7, 7}; + jPanel1.setLayout(jPanel1Layout); + jPanel1.setPreferredSize(new java.awt.Dimension(800, 320)); + { + ledPanel = new JPanel(); + GridBagLayout ledPanelLayout = new GridBagLayout(); + ledPanel.setLayout(ledPanelLayout); + jPanel1.add(ledPanel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + { + JPanel ledPanel1 = new JPanel(new GridLayout(3, 3)); + ledPanel.add(ledPanel1, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + ButtonGroup group = new ButtonGroup(); + String off="None"; + String green="Green"; + String red="Red"; + String both="Orange"; + { + JRadioButton statusOffButton = new JRadioButton(off); + ledPanel1.add(statusOffButton); + statusOffButton.setMnemonic(KeyEvent.VK_N); + statusOffButton.setActionCommand("StatusLED-" + off); + statusOffButton.setSelected(true); + group.add(statusOffButton); + statusOffButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateDisplay("setting Power Color = " + e.getActionCommand() + + "\n"); + setChgGreenLED(roombaCommSerial, false); + setChgRedLED(roombaCommSerial, false); + } + }); + } + { + JRadioButton statusGreenButton = new JRadioButton(green); + ledPanel1.add(statusGreenButton); + statusGreenButton.setMnemonic(KeyEvent.VK_G); + statusGreenButton.setActionCommand("StatusLED-" + green); + group.add(statusGreenButton); + statusGreenButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateDisplay("setting Power Color = " + e.getActionCommand() + + "\n"); + setChgGreenLED(roombaCommSerial, true); + setChgRedLED(roombaCommSerial, false); + } + }); + } + { + JRadioButton statusRedButton = new JRadioButton(red); + ledPanel1.add(statusRedButton); + statusRedButton.setMnemonic(KeyEvent.VK_R); + statusRedButton.setActionCommand("StatusLED-" + red); + group.add(statusRedButton); + statusRedButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateDisplay("setting Power Color = " + e.getActionCommand() + + "\n"); + setChgGreenLED(roombaCommSerial, false); + setChgRedLED(roombaCommSerial, true); + } + }); + } + { + JRadioButton statusBothButton = new JRadioButton(both); + ledPanel1.add(statusBothButton); + statusBothButton.setMnemonic(KeyEvent.VK_O); + statusBothButton.setActionCommand("StatusLED-" + both); + group.add(statusBothButton); + statusBothButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateDisplay("setting Power Color = " + e.getActionCommand() + + "\n"); + setChgGreenLED(roombaCommSerial, true); + setChgRedLED(roombaCommSerial, true); + } + }); + } + { + JButton but_toggleSpot = new JButton(); + ledPanel1.add(but_toggleSpot); + but_toggleSpot.setActionCommand("toggleSpot"); + but_toggleSpot.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_spotOn.png"))); + but_toggleSpot.setPreferredSize(new java.awt.Dimension(46, 50)); + but_toggleSpot.setSize(40, 40); + but_toggleSpot.addActionListener(this); + } + { + JButton but_toggleClean = new JButton(); + ledPanel1.add(but_toggleClean); + but_toggleClean.setActionCommand("toggleClean"); + but_toggleClean.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_cleanOn.png"))); + but_toggleClean.setPreferredSize(new java.awt.Dimension(40, 40)); + but_toggleClean.setSize(40, 40); + but_toggleClean.addActionListener(this); + } + { + JButton but_toggleDirt = new JButton(); + ledPanel1.add(but_toggleDirt); + but_toggleDirt.setActionCommand("toggleDirt"); + but_toggleDirt.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_dirtOn.png"))); + but_toggleDirt.setPreferredSize(new java.awt.Dimension(40, 40)); + but_toggleDirt.setSize(40, 40); + but_toggleDirt.addActionListener(this); + } + { + JButton but_toggleMax = new JButton(); + ledPanel1.add(but_toggleMax); + but_toggleMax.setActionCommand("toggleMax"); + but_toggleMax.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_maxOn.png"))); + but_toggleMax.setSize(40, 40); + but_toggleMax.setPreferredSize(new java.awt.Dimension(40, 40)); + but_toggleMax.addActionListener(this); + } + { + JButton but_toggleDock = new JButton(); + ledPanel1.add(but_toggleDock); + but_toggleDock.setActionCommand("toggleDock"); + but_toggleDock.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_dockOn.png"))); + but_toggleDock.setPreferredSize(new java.awt.Dimension(40, 40)); + but_toggleDock.setSize(40, 40); + but_toggleDock.addActionListener(this); + } + { + JButton but_toggleCheckRobot = new JButton(); + ledPanel1.add(but_toggleCheckRobot); + but_toggleCheckRobot.setActionCommand("toggleCheckRobot"); + but_toggleCheckRobot.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_checkrobotOn.png"))); + but_toggleCheckRobot.setPreferredSize(new java.awt.Dimension(40, 40)); + but_toggleCheckRobot.setSize(40, 40); + but_toggleCheckRobot.addActionListener(this); + } + } + { + powerColorSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, 100); + ledPanel.add(powerColorSlider, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(2, 2, 2, 2), 2, 2)); + powerColorSlider.setPaintTicks(true); + powerColorSlider.setMajorTickSpacing(100); + powerColorSlider.setMinorTickSpacing(25); + powerColorSlider.setPaintLabels(true); + powerColorSlider.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + JSlider src = (JSlider) e.getSource(); + if (!src.getValueIsAdjusting()) { + setPower_color((int) src.getValue()); + updateDisplay("setting Power Color = " + getPower_color() + + "\n"); + setLEDs(roombaCommSerial); + } + } + }); + } + { + JLabel powerColorLabel = new JLabel("PowerColor", JLabel.CENTER); + ledPanel.add(powerColorLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 2, 2)); + } + { + powerColorIntensity = new JSlider(JSlider.HORIZONTAL, 0, 255, 100); + ledPanel.add(powerColorIntensity, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 2, 2)); + powerColorIntensity.setPaintTicks(true); + powerColorIntensity.setMajorTickSpacing(100); + powerColorIntensity.setMinorTickSpacing(25); + powerColorIntensity.setPaintLabels(true); + powerColorIntensity.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + // System.err.println("stateChanged:"+e); + JSlider src = (JSlider) e.getSource(); + if (!src.getValueIsAdjusting()) { + setPower_int((int) src.getValue()); + updateDisplay("setting Power Color Intensity = " + + getPower_int() + "\n"); + setLEDs(roombaCommSerial); + } + } + }); + } + { + JLabel powerColorIntensityLabel = new JLabel("PowerColorIntensity", + JLabel.CENTER); + ledPanel.add(powerColorIntensityLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 2, 2)); + } + ledPanelLayout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1}; + ledPanelLayout.rowHeights = new int[] {7, 7, 7, 7, 7}; + ledPanelLayout.columnWeights = new double[] {0.1}; + ledPanelLayout.columnWidths = new int[] {7}; + ledPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("LEDs"),BorderFactory.createEmptyBorder(5,5,5,5))); + ledPanel.setPreferredSize(new java.awt.Dimension(288, 313)); + } + + { + ctrlPanel = new JPanel(); + jPanel1.add(ctrlPanel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + { + JPanel ctrlPanel1 = new JPanel(); + ctrlPanel.add(ctrlPanel1); + { + JButton but_turnleft = new JButton(); + ctrlPanel1.add(but_turnleft); + but_turnleft.setActionCommand("turnleft"); + but_turnleft.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_turnleft.png"))); + but_turnleft.addActionListener(this); + } + { + JButton but_forward = new JButton(); + ctrlPanel1.add(but_forward); + but_forward.setActionCommand("forward"); + but_forward.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_forward.png"))); + but_forward.addActionListener(this); + } + { + JButton but_turnright = new JButton(); + ctrlPanel1.add(but_turnright); + but_turnright.setActionCommand("turnright"); + but_turnright.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_turnright.png"))); + but_turnright.addActionListener(this); + } + { + JButton but_spinleft = new JButton(); + ctrlPanel1.add(but_spinleft); + but_spinleft.setActionCommand("spinleft"); + but_spinleft.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_spinleft.png"))); + but_spinleft.addActionListener(this); + } + { + JButton but_stop = new JButton(); + ctrlPanel1.add(but_stop); + but_stop.setActionCommand("stop"); + but_stop.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_stop.png"))); + but_stop.addActionListener(this); + } + { + JButton but_spinright = new JButton(); + ctrlPanel1.add(but_spinright); + but_spinright.setActionCommand("spinright"); + but_spinright.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_spinright.png"))); + but_spinright.addActionListener(this); + } + { + ctrlPanel1.add(new JLabel()); + } + { + JButton but_backward = new JButton(); + ctrlPanel1.add(but_backward); + but_backward.setActionCommand("backward"); + but_backward.setIcon(new ImageIcon(getClass().getClassLoader().getResource("com/hackingroomba/roombacomm/images/but_backward.png"))); + but_backward.setPreferredSize(new java.awt.Dimension(55, 78)); + but_backward.addActionListener(this); + } + { + ctrlPanel1.add(new JLabel()); + } + ctrlPanel1.setLayout(new GridLayout(3, 3)); + ctrlPanel1.setPreferredSize(new java.awt.Dimension(194, 199)); + ctrlPanel1.setSize(194, 199); +// ctrlPanel1.setTabTitle(""); + } + { + speedSlider = new JSlider(JSlider.HORIZONTAL, 0, 500, 200); + ctrlPanel.add(speedSlider); + speedSlider.setPaintTicks(true); + speedSlider.setMajorTickSpacing(100); + speedSlider.setMinorTickSpacing(25); + speedSlider.setPaintLabels(true); + speedSlider.addChangeListener(this); + } + { + JLabel sliderLabel = new JLabel(); + ctrlPanel.add(sliderLabel); + sliderLabel.setText("speed (mm/s)"); + sliderLabel.setAlignmentX(JLabel.CENTER); + } + ctrlPanel.setLayout(new BoxLayout(ctrlPanel, BoxLayout.Y_AXIS)); + ctrlPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Movement"),BorderFactory.createEmptyBorder(5,5,5,5))); + } + } + return jPanel1; + } + + private JLabel getJLabelPort() { + if(jLabelPort == null) { + jLabelPort = new JLabel(); + jLabelPort.setText("Port"); + jLabelPort.setPreferredSize(new java.awt.Dimension(39, 33)); + jLabelPort.setLabelFor(getJTextFieldPort()); + jLabelPort.setToolTipText("Set the TCP port"); + } + return jLabelPort; + } + + private JTextField getJTextFieldPort() { + if(jTextFieldPort == null) { + jTextFieldPort = new JTextField(); + jTextFieldPort.setToolTipText("Set the TCP port"); + jTextFieldPort.setText("5001"); + } + return jTextFieldPort; + } + + private void jPanelConfigSerialFocusGained(FocusEvent evt) { + System.out.println("jPanelConfigSerial.focusGained, event="+evt); + //TODO add your code for jPanelConfigSerial.focusGained + // make sure we have a roombaCommSerial object + setCommPorts(); + } + + /** + * + */ + private void setCommPorts() { + System.out.println("setCommPorts-start"); + if (roombaCommSerial == null){ + System.out.println("setCommPorts-roombaCommSerial is null"); + roombaCommSerial = new RoombaCommSerial(this.debug); + } + if (portChoices != null) { + System.out.println("setCommPorts-portChoices object is not null"); + // if the list of ports is empty then try to fill it + if (portChoices.getItemCount() ==0){ + // fill in the comm ports (combo box) with choices. + System.out.println("setCommPorts-getting list of ports"); + String[] ports = roombaCommSerial.listPorts(); + // for now short cutting looking for serial ports to speed up start/stop of the UI + // String[] ports = {"a","b"}; + System.out.println("setCommPorts-found "+ports.length+" serialports"); + for (int i = 0; i < ports.length; i++) { + String s = ports[i]; + System.out.println("setCommPorts-adding ["+i+"] as "+s); + portChoices.addItem(ports[i]); + if (s.equals(roombaCommSerial.getPortname())) { + System.out.println("setCommPorts- setting port as selected due to "+roombaCommSerial.getPortname()); + portChoices.setSelectedItem(s); + } + } + portChoices.validate(); + portChoices.repaint(); + } + } + System.out.println("setCommPorts-start"); + } + + private JLabel getJLabelCOMM() { + if(jLabelCOMM == null) { + jLabelCOMM = new JLabel(); + jLabelCOMM.setText("COM"); + } + return jLabelCOMM; + } + + private JPanel getJPanel2() { + if(jPanel2 == null) { + jPanel2 = new JPanel(); + GridBagLayout jPanel2Layout = new GridBagLayout(); + jPanel2Layout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1}; + jPanel2Layout.rowHeights = new int[] {7, 7, 7, 7, 7, 7}; + jPanel2Layout.columnWeights = new double[] {0.0}; + jPanel2Layout.columnWidths = new int[] {150}; + jPanel2.setLayout(jPanel2Layout); + jPanel2.setBorder(BorderFactory.createTitledBorder(null, "Commands", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION)); + jPanel2.setPreferredSize(new java.awt.Dimension(233, 505)); + jPanel2.add(getJPanelModes(), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + jPanel2.add(getJPanelSounds(), new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + jPanel2.add(getJPanelTestPrograms(), new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + jPanel2.add(getJPanelVacuum(), new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + jPanel2.add(getJPanelPower(), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + jPanel2.add(getJPanel4(), new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 5), 0, 0)); + } + return jPanel2; + } + + private JPanel getJPanel3() { + if(jPanel3 == null) { + jPanel3 = new JPanel(); + jPanel3.setVisible(false); + GridBagLayout jPanel3Layout = new GridBagLayout(); + jPanel3Layout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}; + jPanel3Layout.rowHeights = new int[] {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; + jPanel3Layout.columnWeights = new double[] {0.1}; + jPanel3Layout.columnWidths = new int[] {7}; + jPanel3.setLayout(jPanel3Layout); + { + JButton but_nyi = new JButton(); + jPanel3.add(but_nyi, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi.setText("nyi"); + but_nyi.setVisible(false); + } + { + JButton but_nyi2 = new JButton(); + jPanel3.add(but_nyi2, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi2.setText("nyi2"); + but_nyi2.setVisible(false); + } + { + JButton but_nyi3 = new JButton(); + jPanel3.add(but_nyi3, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi3.setText("nyi3"); + but_nyi3.setVisible(false); + } + { + JButton but_nyi4 = new JButton(); + jPanel3.add(but_nyi4, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi4.setText("nyi4"); + but_nyi4.setVisible(false); + } + { + JButton but_nyi5 = new JButton(); + jPanel3.add(but_nyi5, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi5.setText("nyi5"); + but_nyi5.setVisible(false); + } + { + JButton but_nyi6 = new JButton(); + jPanel3.add(but_nyi6, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi6.setText("nyi6"); + but_nyi6.setVisible(false); + } + { + JButton but_nyi7 = new JButton(); + jPanel3.add(but_nyi7, new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + but_nyi7.setText("nyi7"); + but_nyi7.setVisible(false); + } + { + jButton2 = new JButton(); + jPanel3.add(jButton2, new GridBagConstraints(0, 9, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jButton2.setText("nyi5"); + jButton2.setVisible(false); + } + { + jButton1 = new JButton(); + jPanel3.add(jButton1, new GridBagConstraints(0, 10, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jButton1.setText("nyi6"); + jButton1.setVisible(false); + } + { + jButton3 = new JButton(); + jPanel3.add(jButton3, new GridBagConstraints(0, 11, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jButton3.setText("nyi3"); + jButton3.setVisible(false); + } + { + jButton4 = new JButton(); + jPanel3.add(jButton4, new GridBagConstraints(0, 12, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + jButton4.setText("nyi2"); + jButton4.setVisible(false); + } + } + return jPanel3; + } + + private JPanel getJPanelModes() { + if(jPanelModes == null) { + jPanelModes = new JPanel(); + GridBagLayout jPanelModesLayout = new GridBagLayout(); + jPanelModesLayout.rowWeights = new double[] {0.1, 0.1, 0.1, 0.1, 0.1}; + jPanelModesLayout.rowHeights = new int[] {7, 7, 7, 7, 7}; + jPanelModesLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelModesLayout.columnWidths = new int[] {95, 95}; + jPanelModes.setLayout(jPanelModesLayout); + jPanelModes.setBorder(BorderFactory.createTitledBorder("Modes")); + { + JButton but_spot = new JButton(); + jPanelModes.add(but_spot, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_spot.setText("spot"); + but_spot.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_spot.addActionListener(this); + } + { + JButton but_full = new JButton(); + jPanelModes.add(but_full, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_full.setText("full"); + but_full.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_full.addActionListener(this); + } + { + JButton but_safe = new JButton(); + jPanelModes.add(but_safe, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_safe.setText("safe"); + but_safe.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_safe.addActionListener(this); + } + { + JButton but_dock = new JButton(); + jPanelModes.add(but_dock, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_dock.setText("dock"); + but_dock.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_dock.addActionListener(this); + } + { + JButton but_max = new JButton(); + jPanelModes.add(but_max, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_max.setText("max"); + but_max.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_max.addActionListener(this); + } + { + JButton but_clean = new JButton(); + jPanelModes.add(but_clean, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_clean.setText("clean"); + but_clean.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_clean.addActionListener(this); + } + { + JButton but_wakeup = new JButton(); + jPanelModes.add(but_wakeup, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_wakeup.setText("wakeup"); + but_wakeup.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_wakeup.addActionListener(this); + } + { + JButton but_reset = new JButton(); + jPanelModes.add(but_reset, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_reset.setText("reset"); + but_reset.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_reset.addActionListener(this); + } + { + JButton but_passive = new JButton(); + jPanelModes.add(but_passive, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_passive.setText("passive"); + but_passive.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_passive.addActionListener(this); + } + } + return jPanelModes; + } + + private JPanel getJPanelSounds() { + if(jPanelSounds == null) { + jPanelSounds = new JPanel(); + GridBagLayout jPanelSoundsLayout = new GridBagLayout(); + jPanelSoundsLayout.rowWeights = new double[] {0.1, 0.1}; + jPanelSoundsLayout.rowHeights = new int[] {7, 7}; + jPanelSoundsLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelSoundsLayout.columnWidths = new int[] {95, 95}; + jPanelSounds.setLayout(jPanelSoundsLayout); + jPanelSounds.setBorder(BorderFactory.createTitledBorder("Sounds")); + { + JButton but_OSU = new JButton(); + jPanelSounds.add(but_OSU, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_OSU.setText("OSU"); + but_OSU.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_OSU.addActionListener(this); + } + { + JButton but_beeplo = new JButton(); + jPanelSounds.add(but_beeplo, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_beeplo.setText("beep-lo"); + but_beeplo.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_beeplo.addActionListener(this); + } + { + JButton but_beephi = new JButton(); + jPanelSounds.add(but_beephi, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_beephi.setText("beep-hi"); + but_beephi.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_beephi.addActionListener(this); + } + } + return jPanelSounds; + } + + private JPanel getJPanelTestPrograms() { + if(jPanelTestPrograms == null) { + jPanelTestPrograms = new JPanel(); + GridBagLayout jPanelTestProgramsLayout = new GridBagLayout(); + jPanelTestProgramsLayout.rowWeights = new double[] {0.1}; + jPanelTestProgramsLayout.rowHeights = new int[] {7}; + jPanelTestProgramsLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelTestProgramsLayout.columnWidths = new int[] {95, 95}; + jPanelTestPrograms.setLayout(jPanelTestProgramsLayout); + jPanelTestPrograms.setBorder(BorderFactory.createTitledBorder("Test Programs")); + { + JButton but_TribbleOn = new JButton(); + jPanelTestPrograms.add(but_TribbleOn, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_TribbleOn.setText("Tribble On"); + but_TribbleOn.setPreferredSize(new java.awt.Dimension(93, 26)); + but_TribbleOn.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_TribbleOn.setSize(93, 26); + but_TribbleOn.addActionListener(this); + } + { + JButton but_test = new JButton(); + jPanelTestPrograms.add(but_test, new GridBagConstraints(1, 0, 2, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_test.setText("LogoA.square"); + but_test.setPreferredSize(new java.awt.Dimension(93, 26)); + but_test.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_test.setSize(93, 26); + but_test.addActionListener(this); + } + } + return jPanelTestPrograms; + } + + private JPanel getJPanel4() { + if(jPanelSensors == null) { + jPanelSensors = new JPanel(); + GridBagLayout jPanelSensorsLayout = new GridBagLayout(); + jPanelSensorsLayout.rowWeights = new double[] {0.1}; + jPanelSensorsLayout.rowHeights = new int[] {7}; + jPanelSensorsLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelSensorsLayout.columnWidths = new int[] {95, 95}; + jPanelSensors.setLayout(jPanelSensorsLayout); + jPanelSensors.setBorder(BorderFactory.createTitledBorder(null, "Sensors", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION)); + { + JButton but_chargedata = new JButton(); + jPanelSensors.add(but_chargedata, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0)); + but_chargedata.setText("chargedata"); + but_chargedata.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_chargedata.setPreferredSize(new java.awt.Dimension(93, 26)); + but_chargedata.setSize(93, 26); + but_chargedata.addActionListener(this); + } + { + JButton but_sensors = new JButton(); + jPanelSensors.add(but_sensors, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0)); + but_sensors.setText("sensors"); + but_sensors.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_sensors.setPreferredSize(new java.awt.Dimension(93, 26)); + but_sensors.setSize(93, 26); + but_sensors.addActionListener(this); + } + } + return jPanelSensors; + } + + private JPanel getJPanelVacuum() { + if(jPanelVacuum == null) { + jPanelVacuum = new JPanel(); + GridBagLayout jPanelVacuumLayout = new GridBagLayout(); + jPanelVacuumLayout.rowWeights = new double[] {0.1}; + jPanelVacuumLayout.rowHeights = new int[] {7}; + jPanelVacuumLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelVacuumLayout.columnWidths = new int[] {95, 95}; + jPanelVacuum.setLayout(jPanelVacuumLayout); + jPanelVacuum.setBorder(BorderFactory.createTitledBorder(BorderFactory.createTitledBorder(""), "Vacuum", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION)); + { + JButton but_vacon = new JButton(); + jPanelVacuum.add(but_vacon, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_vacon.setText("vacuum-on"); + but_vacon.setSize(93, 26); + but_vacon.setPreferredSize(new java.awt.Dimension(93, 26)); + but_vacon.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_vacon.addActionListener(this); + } + { + JButton but_vacoff = new JButton(); + but_vacoff.setLayout(null); + jPanelVacuum.add(but_vacoff, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_vacoff.setText("vacuum-off"); + but_vacoff.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_vacoff.setPreferredSize(new java.awt.Dimension(93, 26)); + but_vacoff.setSize(93, 26); + but_vacoff.addActionListener(this); + } + } + return jPanelVacuum; + } + + private JPanel getJPanelPower() { + if(jPanelPower == null) { + jPanelPower = new JPanel(); + GridBagLayout jPanelPowerLayout = new GridBagLayout(); + jPanelPower.setBorder(BorderFactory.createTitledBorder(null, "Power", TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION)); + jPanelPowerLayout.rowWeights = new double[] {0.1}; + jPanelPowerLayout.rowHeights = new int[] {7}; + jPanelPowerLayout.columnWeights = new double[] {0.1, 0.1}; + jPanelPowerLayout.columnWidths = new int[] {95, 95}; + jPanelPower.setLayout(jPanelPowerLayout); + { + JButton but_powerOn = new JButton(); + jPanelPower.add(but_powerOn, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_powerOn.setText("power-on"); + but_powerOn.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_powerOn.addActionListener(this); + } + { + JButton but_power = new JButton(); + jPanelPower.add(but_power, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), 0, 0)); + but_power.setText("power-off"); + but_power.setMargin(new java.awt.Insets(2, 2, 2, 2)); + but_power.addActionListener(this); + } + } + return jPanelPower; + } + + private JTextPane getJTextPane1() { + if(jTextPane1 == null) { + jTextPane1 = new JTextPane(); + jTextPane1.setText("Set the Protocal, then connect via a network or Serial connection.\nThen you can use the Commands, LED's, and/or Movement controls to operate your Roomba."); + jTextPane1.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + jTextPane1.setOpaque(false); + } + return jTextPane1; + } + +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommSerial.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommSerial.java new file mode 100644 index 0000000..c768baf --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommSerial.java @@ -0,0 +1,505 @@ +/* + * RoombaComm Serial Interface + * + * 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 com.hackingroomba.roombacomm; + +import gnu.io.*; +import java.io.*; +import java.util.*; + + +/** + * The serial-port based implementation of RoombaComm. + * Handles both physical RS-232 ports, USB adapter ports like Keyspan + * USA-19HS, and Bluetooth serial port profiles. + * + *

Some code taken from processing.serial.Serial. Thanks guys!

+ * + * The interaction model for setting the port and protocol and WaitForDSR parameters is as follows. + *

+ * On creation, the class initializes the parameters, then tries to read .roomba_config. + * If it can read the config file and parse out the parameters, it sets the parameters to + * the values in the config file. Apps can read the current settings for display using methods + * on the class. Apps can override the settings by accepting user input and setting the + * parameters using methods on the class, or the connect() method. Parameters that are + * changed by the app are re-written in the config file, for use as defaults next run. + * Command-line apps can make these parameters optional, by using the defaults if the + * user doesn't specify them. + * + * SVN id value is $Id: RoombaCommSerial.java 136 2010-04-06 00:07:23Z black.123 $ + */ +public class RoombaCommSerial extends RoombaComm implements SerialPortEventListener +{ +static final int databits = 8; + static final int parity = SerialPort.PARITY_NONE; + static final int stopbits = SerialPort.STOPBITS_1; + /** + * The time to wait in milliseconds after sending sensors command before + * attempting to read + */ + public static int updateSensorsPause = 400; + + /** the serial input stream, normally you don't need access to this */ + public InputStream input; + /** the serial output stream, normally you don't need access to this */ + public OutputStream output; + + /** + * RXTX bombs when flushing output sometimes, so by default do not + * flush the output stream. If the output is too buffered to be + * useful, do: + * roombacomm.comm.flushOutput = true; + * before using it and see if it works. + */ + public boolean flushOutput = false; + byte buffer[] = new byte[32768]; + int bufferLast; + + //int bufferSize = 26; // how big before reset or event firing + //boolean bufferUntil; + //int bufferUntilByte; + + /** + * Let you check to see if a port is in use by another Rooomba + * before trying to use it. + */ + public static boolean isPortInUse( String pname ) { + if (ports != null && pname != null){ + Boolean inuse = (Boolean) ports.get( pname ); + if( inuse !=null ) { + return inuse.booleanValue(); + } + } + return false; + } + + // constructor + public RoombaCommSerial() { + super(); + makePorts(); + readConfigFile(); + } + public RoombaCommSerial(boolean autoupdate) { + super(autoupdate); + makePorts(); + readConfigFile(); + } + public RoombaCommSerial(boolean autoupdate, int updateTime) { + super(autoupdate, updateTime); + makePorts(); + readConfigFile(); + } + + void makePorts() { + if( ports == null ) + ports = Collections.synchronizedMap(new TreeMap()); + } + /** + * Connect to a serial port specified by portid + * doesn't guarantee connection to Roomba, just to serial port + * @param portid name of port, e.g. "/dev/cu.KeySerial1" or "COM3" + * @return true if connect was successful, false otherwise + */ + public boolean connect(String portid) { + logmsg("connecting to port '"+portid+"'"); + portname = portid; + writeConfigFile(portname, getProtocol(), waitForDSR?'Y':'N'); + + if( isPortInUse( portid ) ) { + logmsg("port is in use"); + return false; + } + + connected = open_port(); + + if( connected ) { + // log in the global ports hash if the port is in use now or not + ports.put( portname, new Boolean( connected ) ); + sensorsValid = false; + } + else { + disconnect(); + } + + return connected; + } + public boolean connect(String portid,String protocal) { + String setProt="OI"; + if (protocal.equalsIgnoreCase("SCI")){ + setProt="SCI"; + } + if (protocal.equalsIgnoreCase("OI")){ + setProt="OI"; + } + setProtocol(setProt); + logmsg("connecting to port '"+portid+"' using protocal '"+setProt+"'"); + return connect(portid); + } + + /** + * Disconnect from serial port + */ + public void disconnect() { + connected = false; + + // log in the global ports hash if the port is in use now or not + if (ports != null && portname != null ){ + ports.put( portname, new Boolean( connected ) ); + } + + try { + // do io streams need to be closed first? + if (input != null) input.close(); + if (output != null) output.close(); + } catch (Exception e) { + e.printStackTrace(); + } + input = null; + output = null; + + try { + if (serialPort != null) serialPort.close(); // close the port + } catch (Exception e) { + e.printStackTrace(); + } + serialPort = null; + } + + /** + * subclassed. FIXME: + */ + public boolean send(byte[] bytes) { + try { + output.write(bytes); + if( flushOutput ) output.flush(); // hmm, not sure if a good idea + } catch (Exception e) { // null pointer or serial port dead + e.printStackTrace(); + } + return true; + } + + /** + * This will handle both ints, bytes and chars transparently. + */ + public boolean send(int b) { // will also cover char or byte + try { + output.write(b & 0xff); // for good measure do the & + if( flushOutput ) output.flush(); // hmm, not sure if a good idea + } catch (Exception e) { // null pointer or serial port dead + //errorMessage("send", e); + e.printStackTrace(); + } + return true; + } + + /** + * toggles DD line via serial port DTR (if available) + */ + public void wakeup() { + serialPort.setDTR(false); + pause(500); + serialPort.setDTR(true); + } + + /** + * Update sensors. Block for up to 1000 ms waiting for update + * To use non-blocking, call sensors() and then poll sensorsValid() + */ + public boolean updateSensors() { + System.out.println("updateSensors-start"); + sensorsValid = false; + sensors(); // requests sensor group 0 + for(int i=0; i < 20; i++) { + if( sensorsValid ) { + if ((buffer[1] > 1) || (buffer[1] < 0)) { + sensorsValid = false; + logmsg("updateSensors: received invalid data while attempting to read Roomba sensors!"); + System.out.println("updateSensors: received invalid data while attempting to read Roomba sensors!"); + } else { + logmsg("updateSensors: sensorsValid!"); + System.out.println("updateSensors: sensorsValid!"); + } + + break; + } + logmsg("updateSensors: pausing..."); + System.out.println("updateSensors: pausing..."); + pause( 50 ); + } + if (!sensorsValid){ + logmsg("updateSensors: no data received."); + }; + System.out.println("updateSensors: end"); + return sensorsValid; + } + + /** + * Update sensors. Block for up to 1000 ms waiting for update + * To use non-blocking, call sensors() and then poll sensorsValid() + */ + public boolean updateSensors(int packetcode) { + sensorsValid = false; + sensors(packetcode); + for(int i=0; i < 20; i++) { + if( sensorsValid ) { + logmsg("updateSensors: sensorsValid!"); + break; + } + logmsg("updateSensors: pausing..."); + pause( 50 ); + } + + return sensorsValid; + } + + /** + * called by serialEvent when we have enough bytes to make sensors valid + */ + public void computeSensors() { + sensorsValid = true; + sensorsLastUpdateTime = System.currentTimeMillis(); + computeSafetyFault(); + } + /* + pause(updateSensorsPause); // take a breather to let data come back + sensorsValid = false; // assume the worst, we're gothy + int n = available(); + //logmsg("updateSensors:n="+n); + if( n >= 26) { // there are enough bytes to read + n = readBytes(sensor_bytes); + if( n==26 ) { // did we get enough? + sensorsValid = true; // then everything's good, otherwise bad + computeSafetyFault(); + } + } else { + logmsg("updateSensors:only "+n+" bytes available, not updating sensors"); + } + + //logmsg("buffer contains: "+ buffer ); + return sensorsValid; + */ + + /** + * If this just hangs and never completes on Windows, + * it may be because the DLL doesn't have its exec bit set. + * Why the hell that'd be the case, who knows. + * FIXME: deal more gracefully + * (from processing.serial.Serial) + */ + public String[] listPorts() { + Map ps = Collections.synchronizedMap(new LinkedHashMap()); + //Vector list = new Vector(); + try { + //System.err.println("trying"); + Enumeration portList=null; + try { + portList = CommPortIdentifier.getPortIdentifiers(); + } catch (Exception e) { + //System.err.println("2"); + errorMessage("listPorts1", e); + } + //System.err.println("got port list"); + while (portList.hasMoreElements()) { + CommPortIdentifier portId = (CommPortIdentifier) portList.nextElement(); + logmsg("Found port: " + portId.getName()); + + if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { + String name = portId.getName(); + //list.addElement(name); + Boolean state = (Boolean) ports.get( name ); + if( state==null ) state = new Boolean(false); + ps.put( name, state ); + } + } + } catch (UnsatisfiedLinkError e) { + //System.err.println("1"); + errorMessage("listPorts", e); + } catch (Exception e) { + //System.err.println("2"); + errorMessage("listPorts", e); + } + //System.err.println("move out"); + /* + for( Enumeration e = list.elements(); e.hasMoreElements(); ) { + String p = (String) e.nextElement(); + if( ! ports.containsKey( p ) ) { + ports.put( p, new Boolean(false) ); + } + } + + // DEBUG + System.err.println("ports hashtable:"); + for( Enumeration e = ports.keys(); e.hasMoreElements(); ) { + String p = (String) e.nextElement(); + Boolean b = (Boolean) ports.get(p); + System.err.println("port:"+p+", inuse:"+b); + } + */ + ports = ps; + String outgoing[] = + (String[]) new TreeSet(ports.keySet()).toArray(new String[0]); + + return outgoing; + } + + + public boolean isWaitForDSR() { + return waitForDSR; + } + + public void setWaitForDSR(boolean waitForDSR) { + this.waitForDSR = waitForDSR; + writeConfigFile(portname, getProtocol(), waitForDSR?'Y':'N'); + } + + public String getPortname() { + return portname; + } + + public void setPortname(String p) { + portname = p; + logmsg("Port: " + portname); + writeConfigFile(portname, getProtocol(), waitForDSR?'Y':'N'); + + } + + // ------------------------------------------------------------- + + // below only used internally to this class + // ------------------------------------------------------------- + + /** + * internal method, used by connect() + * FIXME: make it faile more gracefully, recognize bad port + */ + private boolean open_port() { + boolean success = false; + try { + Enumeration portList = CommPortIdentifier.getPortIdentifiers(); + while (portList.hasMoreElements()) { + CommPortIdentifier portId = + (CommPortIdentifier) portList.nextElement(); + + if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { + System.out.println("found " + portId.getName()); + if (portId.getName().equals(portname)) { + logmsg("open_port:"+ portId.getName()); + serialPort = (SerialPort)portId.open("roomba serial", 2000); + //port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); + input = serialPort.getInputStream(); + output = serialPort.getOutputStream(); + serialPort.setSerialPortParams(rate,databits,stopbits,parity); + serialPort.addEventListener(this); + serialPort.notifyOnDataAvailable(true); + logmsg("port "+portname+" opened successfully"); + + if( waitForDSR ) { + int i=40; + while( !serialPort.isDSR() && i-- != 0) { + logmsg("DSR not ready yet"); + pause(150); // 150*40 = 6 seconds + } + success = serialPort.isDSR(); + } else { + success = true; + } + } + } + } + + } catch (Exception e) { + logmsg("connect failed: "+e); + serialPort = null; + input = null; + output = null; + } + + return success; + } + + /** + * callback for SerialPortEventListener + * (from processing.serial.Serial) + */ + synchronized public void serialEvent(SerialPortEvent serialEvent) { + try { + logmsg("serialEvent:"+serialEvent+", Available:"+input.available()); + if (serialEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) { + while (input.available() > 0) { + logmsg("serialEvent: available="+input.available()); + buffer[bufferLast++] = (byte) input.read(); + if( bufferLast == 26 ) { + bufferLast = 0; + System.arraycopy(buffer, 0, sensor_bytes, 0, 26); + computeSensors(); + } + /* + synchronized (buffer) { + if (bufferLast == buffer.length) { + byte temp[] = new byte[bufferLast << 1]; + System.arraycopy(buffer, 0, temp, 0, bufferLast); + buffer = temp; + } + buffer[bufferLast++] = (byte) input.read(); + } + */ + } // while + } + } catch (IOException e) { + errorMessage("serialEvent", e); + } + } + + @SuppressWarnings("unchecked") + public void playSong(RoombaComm roombacomm, String rtttl){ + ArrayList notelist = RTTTLParser.parse( rtttl ); + int songsize = notelist.size(); + // if within the size of a roomba song, make the nsong, then play + if( songsize <= 16 ) { + updateDisplay("creating a song with createSong()", true); + int notearray[] = new int[songsize*2]; + int j=0; + for( int i=0; i< songsize; i++ ) { + Note note = (Note) notelist.get(i); + int sec64ths = note.duration * 64/1000; + notearray[j++] = note.notenum; + notearray[j++] = sec64ths; + } + roombacomm.createSong( 1, notearray ); + roombacomm.playSong( 1 ); + } + // otherwise, try to play it in realtime + else { + updateDisplay("playing song in realtime with playNote()",true); + int fudge = 20; + for( int i=0; i< songsize; i++ ) { + Note note = (Note) notelist.get(i); + int duration = note.duration; + int sec64ths = duration*64/1000; + if( sec64ths < 5 ) sec64ths = 5; + if( note.notenum != 0 ) + roombacomm.playNote( note.notenum, sec64ths ); + roombacomm.pause( duration + fudge ); + } + } + } +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTCPClient.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTCPClient.java new file mode 100644 index 0000000..34c16cf --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTCPClient.java @@ -0,0 +1,451 @@ +/* + * RoombaComm TCP Interface + * + * + * Copyright (c) 2005 Tod E. Kurt, tod@todbot.com + * + * 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 com.hackingroomba.roombacomm; + + +import java.net.*; +import java.io.*; + +import com.hackingroomba.roombacomm.*; +/* + * SVN id value is $Id: RoombaCommTCPClient.java 182 2010-11-02 03:49:10Z bouchier $ + */ +public class RoombaCommTCPClient extends RoombaComm implements Runnable +{ + String host = null; + int port = -1; + + Socket socket; + InputStream input; + OutputStream output; + + Thread thread; + private byte buffer[] = new byte[32768]; + int bufferIndex; + int bufferLast; + + + // constructor + public RoombaCommTCPClient() { + super(); + } + + public RoombaCommTCPClient(RobotConnection rc) + { + super(rc); + } + + public boolean connect(String portid) { + String s[] = portid.split(":"); + if( s.length < 2 ) { + logmsg("bad portid "+portid); + return false; + }else{ + logmsg("portid is ("+portid+")"); + port=0; + host = s[0]; + if (host != null){ + logmsg("Host is ("+host+")"); + }else{ + logmsg("Host is (null)"); + return false; + } + try { + port = Integer.parseInt(s[1]); + } catch( Exception e ) { + logmsg("bad port "+e); + return false; + } + logmsg("connecting to '"+host+":"+port+"'"); + try { + socket = new Socket(host, port); + socket.setKeepAlive(true); + socket.setSoTimeout(30000);// timeout in milliseconds - 30 sec + input = socket.getInputStream(); + output = socket.getOutputStream(); + thread = new Thread(this); + thread.start(); + //this.setConnected(this.updateSensors()); // test if we are connected or not + this.setConnected(true); // TODO: fix this properly + } catch( Exception e ) { + logmsg("connect: "+e); //e.printStackTrace(); + return false; + } + } + return true; + } + + public void disconnect() { + try { + // do io streams need to be closed first? + if (input != null) input.close(); + if (output != null) output.close(); + this.setConnected(false); + } catch (Exception e) { + System.out.print("exception in disconnect"); + e.printStackTrace(); + } + thread = null; + input = null; + output = null; + + try { + if (socket != null) socket.close(); + } catch (Exception e) { + e.printStackTrace(); + } + socket = null; + } + + public boolean send(byte[] bytes) { + // use robotConnection if it exists, else do it the old way + if (robotConnection != null) { + return(robotConnection.send(bytes)); + } + + try { + //logmsg("Send_byte( "+bytes+")"); + output.write(bytes); + output.flush(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + public boolean send(int b) { // will also cover char + // use robotConnection if it exists, else do it the old way + if (robotConnection != null) { + return(robotConnection.send(b)); + } + + try { + //logmsg("Send_( "+b+" & 0xff)"); + output.write(b & 0xff); // for good measure do the & + output.flush(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return false; + } + + public void wakeup() { + logmsg("wakup unimplemented"); + } + + public boolean updateSensorsOLD() { + sensorsValid = false; + for(int i=0; i < 20; i++) { + sensors(); + if( sensorsValid ) { + if ((buffer[1] > 1) || (buffer[1] < 0)) { + sensorsValid = false; + logmsg("updateSensors: received invalid data while attempting to read Roomba sensors!"); + } else { + logmsg("updateSensors: sensorsValid!"); + } + break; + } + logmsg("updateSensors: pausing..."); + pause( 50 ); + } + return sensorsValid; + } + + // copied from RoombaCommSerial + public void computeSensors() { + sensorsValid = true; + sensorsLastUpdateTime = System.currentTimeMillis(); + computeSafetyFault(); + } + + public String[] listPorts() { + String s[] = new String[0]; + if( host !=null && port !=-1 ) { + String p[] = { host+":"+port }; + return p; + } + return s; + } + + + ////////////////////////////////////////////////////////////////// + + /** + * + */ + public void run() { + bufferLast = 0; + while (Thread.currentThread() == thread) { + try { + while (input != null) { + buffer[bufferLast++] = (byte) input.read(); // this will block - wait until there's something + //logmsg("TCP received sensor data: " + buffer[bufferLast-1]); + if( bufferLast == readRequestLength ) { + bufferLast = 0; + System.arraycopy(buffer, 0, sensor_bytes, 0, 26); + sensorsValid = true; + //computeSensors(); + } + +// synchronized (getBuffer()) { +// if (bufferLast == getBuffer().length) { +// byte temp[] = new byte[bufferLast << 1]; +// System.arraycopy(getBuffer(), 0, temp, 0, bufferLast); +// setBuffer(temp); +// } +// getBuffer()[bufferLast++] = (byte) input.read(); +// } + } + +// try { +// // uhh.. not sure what's best here.. since blocking, +// // do we need to worry about sleeping much? or is this +// // gonna try to slurp cpu away from the main applet? +// Thread.sleep(10); +// } catch (InterruptedException ex) { } + + } catch (IOException e) { + System.err.println("run:"+e); + } + } + } + + /** + * Returns the number of bytes that have been read from serial + * and are waiting to be dealt with by the user. + */ + public int available() { + return (bufferLast - bufferIndex); + } + + /** + * Ignore all the bytes read so far and empty the buffer. + */ + public void clear() { + bufferLast = 0; + bufferIndex = 0; + } + + /** + * Returns a number between 0 and 255 for the next byte that's + * waiting in the buffer. + * Returns -1 if there was no byte (although the user should + * first check available() to see if things are ready to avoid this) + */ + public int read() { + if (bufferIndex == bufferLast) return -1; + + synchronized (getBuffer()) { + int outgoing = getBuffer()[bufferIndex++] & 0xff; + if (bufferIndex == bufferLast) { // rewind + bufferIndex = 0; + bufferLast = 0; + } + return outgoing; + } + } + + + /** + * Returns the next byte in the buffer as a char. + * Returns -1, or 0xffff, if nothing is there. + */ + public char readChar() { + if (bufferIndex == bufferLast) return (char)(-1); + return (char) read(); + } + + + /** + * Return a byte array of anything that's in the serial buffer. + * Not particularly memory/speed efficient, because it creates + * a byte array on each read, but it's easier to use than + * readBytes(byte b[]) (see below). + */ + public byte[] readBytes() { + if (bufferIndex == bufferLast) return null; + + synchronized (getBuffer()) { + int length = bufferLast - bufferIndex; + byte outgoing[] = new byte[length]; + System.arraycopy(getBuffer(), bufferIndex, outgoing, 0, length); + + bufferIndex = 0; // rewind + bufferLast = 0; + return outgoing; + } + } + + /** + * Grab whatever is in the serial buffer, and stuff it into a + * byte buffer passed in by the user. This is more memory/time + * efficient than readBytes() returning a byte[] array. + * + * Returns an int for how many bytes were read. If more bytes + * are available than can fit into the byte array, only those + * that will fit are read. + */ + public int readBytes(byte outgoing[]) { + if (bufferIndex == bufferLast) return 0; + + synchronized (getBuffer()) { + int length = bufferLast - bufferIndex; + if (length > outgoing.length) length = outgoing.length; + System.arraycopy(getBuffer(), bufferIndex, outgoing, 0, length); + + bufferIndex += length; + if (bufferIndex == bufferLast) { + bufferIndex = 0; // rewind + bufferLast = 0; + } + return length; + } + } + + + /** + * Reads from the serial port into a buffer of bytes up to and + * including a particular character. If the character isn't in + * the serial buffer, then 'null' is returned. + */ + public byte[] readBytesUntil(int interesting) { + if (bufferIndex == bufferLast) return null; + byte what = (byte)interesting; + + synchronized (getBuffer()) { + int found = -1; + for (int k = bufferIndex; k < bufferLast; k++) { + if (getBuffer()[k] == what) { + found = k; + break; + } + } + if (found == -1) return null; + + int length = found - bufferIndex + 1; + byte outgoing[] = new byte[length]; + System.arraycopy(getBuffer(), bufferIndex, outgoing, 0, length); + + bufferIndex = 0; // rewind + bufferLast = 0; + return outgoing; + } + } + + + /** + * Reads from the serial port into a buffer of bytes until a + * particular character. If the character isn't in the serial + * buffer, then 'null' is returned. + * + * If outgoing[] is not big enough, then -1 is returned, + * and an error message is printed on the console. + * If nothing is in the buffer, zero is returned. + * If 'interesting' byte is not in the buffer, then 0 is returned. + */ + public int readBytesUntil(int interesting, byte outgoing[]) { + if (bufferIndex == bufferLast) return 0; + byte what = (byte)interesting; + + synchronized (getBuffer()) { + int found = -1; + for (int k = bufferIndex; k < bufferLast; k++) { + if (getBuffer()[k] == what) { + found = k; + break; + } + } + if (found == -1) return 0; + + int length = found - bufferIndex + 1; + if (length > outgoing.length) { + System.err.println("readBytesUntil() byte buffer is" + + " too small for the " + length + + " bytes up to and including char " + interesting); + return -1; + } + //byte outgoing[] = new byte[length]; + System.arraycopy(getBuffer(), bufferIndex, outgoing, 0, length); + + bufferIndex += length; + if (bufferIndex == bufferLast) { + bufferIndex = 0; // rewind + bufferLast = 0; + } + return length; + } + } + + + /** + * Return whatever has been read from the serial port so far + * as a String. It assumes that the incoming characters are ASCII. + * + * If you want to move Unicode data, you can first convert the + * String to a byte stream in the representation of your choice + * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. + */ + public String readString() { + if (bufferIndex == bufferLast) return null; + return new String(readBytes()); + } + + + /** + * Combination of readBytesUntil and readString. See caveats in + * each function. Returns null if it still hasn't found what + * you're looking for. + * + * If you want to move Unicode data, you can first convert the + * String to a byte stream in the representation of your choice + * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. + */ + public String readStringUntil(int interesting) { + byte b[] = readBytesUntil(interesting); + if (b == null) return null; + return new String(b); + } + + /** + * @param buffer the buffer to set + */ + public void setBuffer(byte buffer[]) { + this.buffer = buffer; + } + + /** + * @return the buffer + */ + public byte[] getBuffer() { + return buffer; + } + + +} + + diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTest.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTest.java new file mode 100644 index 0000000..482f064 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTest.java @@ -0,0 +1,102 @@ +/* + * RoombaCommtest -- small GUI to test out RoombaComm + * + * 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 com.hackingroomba.roombacomm; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; + +/** + * A simple wrapper for RoombaCommPanel. + * + */ +public class RoombaCommTest extends JFrame implements WindowListener { + + RoombaCommPanel rcPanel; + boolean hwhandshake = false; + boolean debug = false; + + public static void main(String[] args) { + new RoombaCommTest(args); + } + + public RoombaCommTest(String[] args) { + super("RoombaCommTest"); + addWindowListener(this); + + for( int i=0; i < args.length; i++ ) { + if( args[i].endsWith("hwhandshake") ) + hwhandshake = true; + else if (args[i].endsWith("debug")) + debug = true; + } + + rcPanel = new RoombaCommPanel(debug); + + rcPanel.setShowHardwareHandhake( hwhandshake ); + + Container content = getContentPane(); + //content.setBackground(Color.lightGray); + content.add( rcPanel ); // , BorderLayout.CENTER ); + + setResizable(false); + pack(); + setVisible(true); + } + + + /** implement windowlistener */ + public void windowClosing(WindowEvent event) { + rcPanel.disconnect(); + System.exit(0); + } + /** implement windowlistener */ + public void windowClosed(WindowEvent event) { + // do nothing + } + /** implement windowlistener */ + public void windowOpened(WindowEvent event) { + // do nothing + } + /** implement windowlistener */ + public void windowActivated(WindowEvent event) { + // do nothing + } + /** implement windowlistener */ + public void windowDeactivated(WindowEvent event) { + // do nothing + } + /** implement windowlistener */ + public void windowIconified(WindowEvent event) { + // do nothing + } + /** implement windowlistener */ + public void windowDeiconified(WindowEvent event) { + // do nothing + } + +} + diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTestOld.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTestOld.java new file mode 100644 index 0000000..00cc78b --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaCommTestOld.java @@ -0,0 +1,387 @@ +// +// RoombaCommtest -- small GUI to test out RoombaComm +// +// Tod E. Kurt, tod@todbot.com +// +// + +package com.hackingroomba.roombacomm; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; + +public class RoombaCommTestOld extends JFrame implements ActionListener,ChangeListener,WindowListener { + static final String VERSION = "DO NOT USE"; + + JPanel ctrlPanel, selectPanel, buttonPanel, displayPanel; + JComboBox portChoices; + JTextArea displayText; + JButton connectButton; + JSlider speedSlider; + + RoombaComm roombacomm; + + public static void main(String[] args) { + new RoombaCommTestOld(); + } + + public RoombaCommTestOld() { + super("RoombaCommTestOld"); + setResizable(false); + //setNativeLookAndFeel(); + addWindowListener(this); + + roombacomm = new RoombaCommSerial(); + + Container content = getContentPane(); + content.setBackground(Color.lightGray); + + makeSelectPanel(); + content.add( selectPanel, BorderLayout.NORTH ); + + makeCtrlPanel(); + content.add( ctrlPanel, BorderLayout.EAST ); + + makeButtonPanel(); + content.add( buttonPanel, BorderLayout.CENTER ); + + makeDisplayPanel(); + content.add( displayPanel, BorderLayout.SOUTH ); + + pack(); + setVisible(true); + displayText.append("RoombaCommTestOld, version "+VERSION+"\n"); + } + + /** implement actionlistener */ + public void actionPerformed(ActionEvent event) { + String action = event.getActionCommand(); + if( "comboBoxChanged".equals(action) ) { + return; + } + displayText.append(action+"\n"); + if( "connect".equals(action) ) { + roombacomm.debug=true; + if( roombacomm.connected() ) { + roombacomm.disconnect(); // just in case + connectButton.setText(" connect "); + return; + } + else { + displayText.append("could not connect...darn\n"); + } + connectButton.setText("connecting"); + String portname = (String) portChoices.getSelectedItem(); + if( ! roombacomm.connect( portname ) ) { + displayText.append("Couldn't connect to "+portname+"\n"); + return; + } + displayText.append("Roomba startup\n"); + roombacomm.startup(); + roombacomm.control(); + roombacomm.playNote( 72, 10 ); // C + roombacomm.pause( 200 ); + connectButton.setText("disconnect"); + displayText.append("Roomba connected\n"); + return; + } + + // stop right here if we're not connected + if( !roombacomm.connected() ) { + displayText.append("not connected!\n"); + return; + } + + 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( "test".equals(action) ) { + displayText.append("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 ); + + displayText.append("Spinning left, then right\n"); + roombacomm.spinLeft(); + roombacomm.pause(1000); + roombacomm.spinRight(); + roombacomm.pause(1000); + roombacomm.stop(); + + displayText.append("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 ); // C0 + 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( "sensors".equals(action) ) { + if( roombacomm.updateSensors() ) + displayText.append( roombacomm.sensorsAsString()+"\n"); + else + displayText.append("couldn't read Roomba. Is it connected?\n"); + } + } + + /** implement ChangeListener, for the slider */ + public void stateChanged(ChangeEvent e) { + //System.err.println("stateChanged:"+e); + JSlider src = (JSlider)e.getSource(); + if (!src.getValueIsAdjusting()) { + int speed = (int)src.getValue(); + speed = (speed<1) ? 1 : speed; // don't allow zero speed + displayText.append("setting speed = "+speed+"\n"); + roombacomm.setSpeed(speed); + } + } + + /** + * + */ + void makeCtrlPanel() { + JPanel ctrlPanel1 = new JPanel(new GridLayout(3,3)); + + ctrlPanel1.add(new JLabel()); + JButton but_forward = + new JButton(createImageIcon("images/forward.png", "forward")); + ctrlPanel1.add( but_forward, BorderLayout.NORTH ); + ctrlPanel1.add(new JLabel()); + + JButton but_spinleft = + new JButton(createImageIcon("images/spinleft.png", "spinleft")); + ctrlPanel1.add( but_spinleft, BorderLayout.WEST ); + JButton but_stop = + new JButton(createImageIcon("images/stop.png", "stop")); + ctrlPanel1.add( but_stop, BorderLayout.CENTER ); + JButton but_spinright = + new JButton( createImageIcon("images/spinright.png", "spinright")); + ctrlPanel1.add( but_spinright, BorderLayout.EAST); + + ctrlPanel1.add(new JLabel()); + JButton but_backward = + new JButton(createImageIcon("images/backward.png", "backward")); + ctrlPanel1.add( but_backward, BorderLayout.SOUTH ); + ctrlPanel1.add(new JLabel()); + + JLabel sliderLabel = new JLabel("speed (mm/s)", JLabel.CENTER); + speedSlider = new JSlider(JSlider.HORIZONTAL, 0, 500, 200 ); + speedSlider.setPaintTicks(true); + speedSlider.setMajorTickSpacing(100); + speedSlider.setMinorTickSpacing(25); + speedSlider.setPaintLabels(true); + speedSlider.addChangeListener(this); + + 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( speedSlider ); + ctrlPanel.add( sliderLabel ); + + but_spinleft.setActionCommand("spinleft"); + but_spinright.setActionCommand("spinright"); + but_forward.setActionCommand("forward"); + but_backward.setActionCommand("backward"); + but_stop.setActionCommand("stop"); + but_spinleft.addActionListener(this); + but_spinright.addActionListener(this); + but_forward.addActionListener(this); + but_backward.addActionListener(this); + but_stop.addActionListener(this); + } + + /** + * + */ + 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"); + + //Add a border around the select panel. + selectPanel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder("Select Roomba Port"), BorderFactory.createEmptyBorder(5,5,5,5))); + + selectPanel.add(portChoices); + selectPanel.add(connectButton); + + //Listen to events from the combo box. + portChoices.addActionListener(this); + connectButton.addActionListener(this); + } + + /** + * + */ + void makeButtonPanel() { + //buttonPanel = new JPanel(); + //buttonPanel.setLayout( new BoxLayout(buttonPanel, BoxLayout.Y_AXIS)); + 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"); + buttonPanel.add( but_reset ); + but_reset.addActionListener(this); + + JButton but_test = new JButton("test"); + buttonPanel.add( but_test ); + but_test.addActionListener(this); + + JButton but_power = new JButton("power-off"); + buttonPanel.add( but_power ); + but_power.addActionListener(this); + + JButton but_wakeup = new JButton("wakeup"); + buttonPanel.add( but_wakeup ); + but_wakeup.addActionListener(this); + + JButton but_beeplo = new JButton("beep-lo"); + buttonPanel.add( but_beeplo ); + but_beeplo.addActionListener(this); + + JButton but_beephi = new JButton("beep-hi"); + buttonPanel.add( but_beephi ); + but_beephi.addActionListener(this); + + JButton but_clean = new JButton("clean"); + buttonPanel.add( but_clean ); + but_clean.addActionListener(this); + + JButton but_spot = new JButton("spot"); + buttonPanel.add( but_spot ); + but_spot.addActionListener(this); + + JButton but_vacon = new JButton("vacuum-on"); + buttonPanel.add( but_vacon ); + but_vacon.addActionListener(this); + + JButton but_vacoff = new JButton("vacuum-off"); + buttonPanel.add( but_vacoff ); + but_vacoff.addActionListener(this); + + JButton but_sensors = new JButton("sensors"); + buttonPanel.add( but_sensors ); + 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.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 ); + + } + + /** implement windowlistener */ + public void windowClosing(WindowEvent event) { + if( roombacomm.connected() ) + roombacomm.disconnect(); + System.exit(0); + } + /** implement windowlistener */ + public void windowClosed(WindowEvent event) { + // do nothing + } + /** implement windowlistener */ + public void windowOpened(WindowEvent event) { + // do nothing + } + /** implement windowlistener */ + public void windowActivated(WindowEvent event) { + // do nothing + } + /** implement windowlistener */ + public void windowDeactivated(WindowEvent event) { + // do nothing + } + /** implement windowlistener */ + public void windowIconified(WindowEvent event) { + // do nothing + } + /** implement windowlistener */ + public void windowDeiconified(WindowEvent event) { + // do nothing + } + + + /** Returns an ImageIcon, or null if the path was invalid. */ + protected static ImageIcon createImageIcon(String path, + String description) { + java.net.URL imgURL = RoombaCommTestOld.class.getResource(path); + if (imgURL != null) { + return new ImageIcon(imgURL, description); + } else { + System.err.println("Couldn't find file: " + path); + return null; + } + } + + +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorder.java b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorder.java new file mode 100644 index 0000000..81cd305 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/RoombaRecorder.java @@ -0,0 +1,591 @@ +/* + * RoombaRecorder -- + * + * 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 com.hackingroomba.roombacomm; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; +import javax.swing.filechooser.*; +import java.util.*; +import java.io.*; + +/** + * A simple wrapper for multiple MacroRecorderPanels + * + */ +public class RoombaRecorder extends JFrame implements WindowListener,ActionListener { + + static final String helpMsg = ""+ + //"

"+ + "

Roomba Movement Keyboard Shortcuts

"+ + "
    "+ + "
  • arrow keys -- move Roomba"+ + "
  • space bar -- stop Roomba"+ + "
  • L -- blink Roomba LEDs"+ + "
  • V -- toggle Roomba vacuum"+ + "
  • T -- test Roomba"+ + "
  • R -- reset Roomba"+ + "
  • 1,2,3,4 -- adjust speed"+ + "
"+ + "

Application Control Keyboard Shortcuts

"+ + "
    "+ + "
  • cmd-T -- open new tab"+ + "
  • cmd-W -- close current tab"+ + "
  • cmd-arrow-left/right -- cycle through tabs"+ + "
  • cmd-X/C/V -- cut/copy/paste events between tabs"+ + "
"+ + ""; + + JPanel contentPane; + JPanel recordPanel; + JTabbedPane tabbedPane; + JButton stopButton,recordButton,playButton; + JCheckBox loopButton; + JCheckBox playAllButton; + JFileChooser fc; + File file; + boolean hwhandshake = false; + int maxRoombas = 16; + int lastTab = 0; + HashMap clipboard; + RoombaRecorderPanel rrpanel; // just to make some of code lines shorter + boolean movingForward = false; // is roomba moving forward or not + boolean vacuuming = false; // is roomba vacuuming or not + int turnval = 0; + + public static void main(String[] args) { + new RoombaRecorder(args); + } + + public RoombaRecorder(String[] args) { + super("RoombaRecorder"); + addWindowListener(this); + + for(int i=0; i < args.length; i++) { + if(args[i].endsWith("hwhandshake")) + hwhandshake = true; + } + + clipboard = new HashMap(); + fc = new JFileChooser(); + + setResizable(false); + + makeMenu(); + + makeRecordPanel(); + + // Create and set up the content pane. + tabbedPane = new JTabbedPane(); + tabbedPane.setOpaque(true); //content panes must be opaque + openNewTab(); + + contentPane = new JPanel(new BorderLayout()); + + contentPane.add(recordPanel, BorderLayout.NORTH); + contentPane.add(tabbedPane, BorderLayout.CENTER); + + //Container content = getContentPane(); + setContentPane(contentPane); + + addBindings(); + bindKeys(); + + // Display the window. + pack(); //setSize(400, 400); + setVisible(true); + + //setFocusableWindowState(false); // prevent focus + } + + void makeMenu() { + JMenuBar menubar = new JMenuBar(); + JMenuItem item; + + JMenu file = new JMenu("File"); + JMenu edit = new JMenu("Edit"); + JMenu help = new JMenu("Help"); + + file.setMnemonic(KeyEvent.VK_F); + edit.setMnemonic(KeyEvent.VK_E); + help.setMnemonic(KeyEvent.VK_H); + + item = new JMenuItem("New Tab"); + item.addActionListener(this); + file.add(item); + item = new JMenuItem("Close Tab"); + item.addActionListener(this); + file.add(item); + item = new JMenuItem("Open Set"); + item.addActionListener(this); + file.add(item); + item = new JMenuItem("Save Set"); + item.addActionListener(this); + file.add(item); + item = new JMenuItem("Quit"); + item.addActionListener(this); + file.add(item); + + item = new JMenuItem("Cut"); + item.addActionListener(this); + edit.add(item); + item = new JMenuItem("Copy"); + item.addActionListener(this); + edit.add(item); + item = new JMenuItem("Paste"); + item.addActionListener(this); + edit.add(item); + + item = new JMenuItem("Help"); + item.addActionListener(this); + help.add(item); + + menubar.add(file); + menubar.add(edit); + menubar.add(help); + + setJMenuBar(menubar); + } + + void makeRecordPanel() { + recordPanel = new JPanel(); + recordPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Action Record"), BorderFactory.createEmptyBorder())); //1,1,1,1))); + stopButton = new JButton(createImageIcon("images/but_transport_stop.png","stop")); + stopButton.setActionCommand("stop"); + stopButton.addActionListener(this); + recordPanel.add(stopButton); + playButton = new JButton(createImageIcon("images/but_transport_play.png","play")); + playButton.setSelectedIcon(createImageIcon("images/but_transport_play_push.png","play")); + playButton.setActionCommand("play"); + playButton.addActionListener(this); + recordPanel.add(playButton); + recordButton = new JButton(createImageIcon("images/but_transport_record.png","record")); + recordButton.setSelectedIcon(createImageIcon("images/but_transport_record_push.png","record")); + recordButton.setActionCommand("record"); + recordButton.addActionListener(this); + recordPanel.add(recordButton); + + loopButton = new JCheckBox("loop"); + loopButton.setText("loop"); + loopButton.setActionCommand("loop"); + loopButton.addActionListener(this); + recordPanel.add(loopButton); + + playAllButton = new JCheckBox("play all"); + playAllButton.addActionListener(this); + recordPanel.add(playAllButton); + } + + void addBindings() { + InputMap inputMap = contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + KeyStroke key; + + //key = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0); + //contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).remove(key); + + key = KeyStroke.getKeyStroke('T',KeyEvent.CTRL_MASK); + inputMap.put(key, "new-tab"); + key = KeyStroke.getKeyStroke('T',KeyEvent.META_MASK); + inputMap.put(key, "new-tab"); + key = KeyStroke.getKeyStroke('N',KeyEvent.CTRL_MASK); + inputMap.put(key, "new-tab"); + key = KeyStroke.getKeyStroke('N',KeyEvent.META_MASK); + inputMap.put(key, "new-tab"); + + key = KeyStroke.getKeyStroke('W',KeyEvent.CTRL_MASK); + inputMap.put(key, "close-tab"); + key = KeyStroke.getKeyStroke('W',KeyEvent.META_MASK); + inputMap.put(key, "close-tab"); + + key = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,KeyEvent.CTRL_MASK); + inputMap.put(key, "right-tab"); + key = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,KeyEvent.CTRL_MASK); + inputMap.put(key, "left-tab"); + key = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,KeyEvent.META_MASK); + inputMap.put(key, "right-tab"); + key = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,KeyEvent.META_MASK); + inputMap.put(key, "left-tab"); + + key = KeyStroke.getKeyStroke('X',KeyEvent.CTRL_MASK); + inputMap.put(key, "cut"); + key = KeyStroke.getKeyStroke('X',KeyEvent.META_MASK); + inputMap.put(key, "cut"); + key = KeyStroke.getKeyStroke('C',KeyEvent.CTRL_MASK); + inputMap.put(key, "copy"); + key = KeyStroke.getKeyStroke('C',KeyEvent.META_MASK); + inputMap.put(key, "copy"); + key = KeyStroke.getKeyStroke('V',KeyEvent.CTRL_MASK); + inputMap.put(key, "paste"); + key = KeyStroke.getKeyStroke('V',KeyEvent.META_MASK); + inputMap.put(key, "paste"); + + ActionMap actionMap = contentPane.getActionMap(); + actionMap.put("new-tab", new AbstractAction() { + public void actionPerformed(ActionEvent e) { + openNewTab(); + } }); + actionMap.put("close-tab",new AbstractAction() { + public void actionPerformed(ActionEvent e) { + closeCurrentTab(); + } }); + actionMap.put("left-tab",new AbstractAction() { + public void actionPerformed(ActionEvent e) { + cycleTabLeft(); + } }); + actionMap.put("right-tab",new AbstractAction() { + public void actionPerformed(ActionEvent e) { + cycleTabRight(); + } }); + actionMap.put("cut",new AbstractAction() { + public void actionPerformed(ActionEvent e) { + clipboard = getCurrentTab().cut(); + } }); + actionMap.put("copy",new AbstractAction() { + public void actionPerformed(ActionEvent e) { + clipboard = getCurrentTab().copy(); + } }); + actionMap.put("paste",new AbstractAction() { + public void actionPerformed(ActionEvent e) { + getCurrentTab().paste(clipboard); + } }); + } + + /** + * Bind keys to actions using a custom KeyEventPostProcessor + * (fixme: why can't this go in the panel?) + */ + void bindKeys() { + // ahh, the succinctness of java + KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + + kfm.addKeyEventDispatcher( new KeyEventDispatcher() { + public boolean dispatchKeyEvent(KeyEvent e) { + String action=null; + if(e.getID() != KeyEvent.KEY_PRESSED) + return false; + if(e.getModifiers() != 0) + return false; + switch(e.getKeyCode()) { + case KeyEvent.VK_UP: + action = "forward"; + movingForward=true; + turnval = 0; + break; + case KeyEvent.VK_DOWN: + action="backward"; + movingForward=false; + turnval = 0; + break; + case KeyEvent.VK_LEFT: + if(movingForward) { + turnval = (turnval==0) ? 100 : turnval-turnval/2; + if(turnval<10) { action="spinleft"; turnval=0; } + else action = "turn"+turnval; + } + else action = "spinleft"; + //action = (movingForward) ? "turn100" : "spinleft"; + break; + case KeyEvent.VK_RIGHT: + if(movingForward) { + turnval = (turnval==0) ? -100 : turnval-turnval/2; + if(turnval>-10) { action="spinright"; turnval=0; } + else action = "turn"+turnval; + } + else action = "spinright"; + //action = (movingForward) ? "turn-100" : "spinright"; + break; + case KeyEvent.VK_SPACE: + action="stop"; + movingForward=false; + turnval = 0; + break; + case KeyEvent.VK_L: + action="blink-leds"; + break; + case KeyEvent.VK_R: + action="reset"; + break; + case KeyEvent.VK_T: + action="test"; + break; + case KeyEvent.VK_V: + vacuuming = !vacuuming; + action = (vacuuming) ? "vacuum-on":"vacuum-off"; + break; + case KeyEvent.VK_1: + action="50"; + break; + case KeyEvent.VK_2: + action="100"; + break; + case KeyEvent.VK_3: + action="150"; + break; + case KeyEvent.VK_4: + action="250"; + break; + case KeyEvent.VK_5: + action="400"; + break; + case KeyEvent.VK_6: + action="500"; + break; + } + + if(action!=null) + getCurrentTab().actionPerformed(new ActionEvent(this,0,action)); + //System.out.println("process '"+e.getKeyCode()+"' "+e); + return true; + } + }); + } + + /** Implement Actionlistener */ + public void actionPerformed(ActionEvent event) { + String action = event.getActionCommand(); + //System.out.println("action:"+action+", event: "+event); + if("stop".equals(action)) { + playButton.setSelected(false); + recordButton.setSelected(false); + for(int i=0; i 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); + } + +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/SimpleTest.java b/roombacomm-client/src/com/hackingroomba/roombacomm/SimpleTest.java new file mode 100644 index 0000000..3c55daa --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/SimpleTest.java @@ -0,0 +1,148 @@ +/* + * roombacomm.SimpleTest + * + * Copyright (c) 2005 Tod E. Kurt, tod@todbot.com + * + * 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 com.hackingroomba.roombacomm; + +/** + A simple test of RoombaComm and RoombaCommSerial functionality. +

+ Run it with something like:

+    java roombacomm.SimpleTest /dev/cu.KeySerial1
+ Usage: + roombacomm.SimpleTest serialportname [protocol] [options]
+ where: + protocol (optional) is SCI or OI + [options] can be one or more of: + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -nohwhandshake -- don't use hardware-handshaking + -flush -- flush on sends(), normally not needed + *
+ * + */ +public class SimpleTest { + + static String usage = + "Usage: \n"+ + " roombacomm.SimpleTest [protocol] [options]\n" + + "where:\n"+ + "protocol (optional) is SCI or OI\n"+ + "[options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + " -nohwhandshake -- don't use hardware-handshaking\n"+ + " -flush -- flush on sends(), normally not needed\n"+ + "\n"; + static boolean debug = false; + static boolean hwhandshake = false; + static boolean flush = false; + + public static void main(String[] args) { + if( args.length == 0 ) { + System.out.println( usage ); + System.exit(0); + } + + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(); + for( int i=1; i < args.length; i++ ) { + if (args[i].equals("SCI") || (args[1].equals("OI"))) { + roombacomm.setProtocol(args[i]); + } else if( args[i].endsWith("debug") ) + debug = true; + else if( args[i].endsWith("nohwhandshake") ) + roombacomm.setWaitForDSR(false); + else if( args[i].endsWith("hwhandshake") ) + roombacomm.setWaitForDSR(true); + else if( args[i].endsWith("flush") ) + flush = true; + } + + + roombacomm.debug = debug; + roombacomm.flushOutput = flush; + + String portlist[] = roombacomm.listPorts(); + System.out.println("Available ports:"); + for(int i=0;i + Run it with something like:
+    java roombacomm.Spiral /dev/cu.KeySerial1
+ Usage: + roombacomm.Spiral [protocol] [options]
+ where: protocol (optional) is SCI or OI + [options] can be one or more of: + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -nohwhandshake -- don't use hardware-handshaking +
+ + */ +public class Spiral { + + static String usage = + "Usage: \n"+ + " roombacomm.Spiral [protocol] [options]\n" + + "where: protocol (optional) is SCI or OI\n" + + "[options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + " -nohwhandshake -- don't use hardware-handshaking\n"+ + "\n"; + static boolean debug = false; + static boolean hwhandshake = false; + + public static void main(String[] args) { + if( args.length == 0 ) { + System.out.println( usage ); + System.exit(0); + } + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(); + for( int i=1; i 410 ) { + dr = -dr; + } + + done = keyIsPressed(); + + roombacomm.pause( pausetime ); + } + + roombacomm.stop(); + + System.out.println("Disconnecting"); + roombacomm.disconnect(); + + System.out.println("Done"); + } + + /** check for keypress, return true if so */ + public static boolean keyIsPressed() { + boolean press = false; + try { + if( System.in.available() != 0 ) { + System.out.println("key pressed"); + press = true; + } + } catch( IOException ioe ) { } + return press; + } +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro1.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro1.java new file mode 100644 index 0000000..6bac0ce --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro1.java @@ -0,0 +1,139 @@ +/* + * roombacomm.Spiro1 -- a Spirograph-like example + * + * 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 com.hackingroomba.roombacomm; + +import java.io.*; + +/** + A Spirograph-like example +

+ Run it with something like:

+   java roombacomm.Spiro1 /dev/cu.KeySerial1 velocity radius waittime
+ Usage: \n"+ + roombacomm.Spiro1 [protocol] [options]
+ where: + protocol (optional) is SCI or OI + velocity and radius in mm, waittime in milliseconds + [options] can be one or more of: + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -nohwhandshake -- don't use hardware-handshaking +
+ */ +public class Spiro1 { + + static String usage = + "Usage: \n"+ + " roombacomm.Spiro1 [protocol] [options]\n" + + "where: protocol (optional) is SCI or OI\n" + + "velocity and radius in mm, waittime in milliseconds\n"+ + "[options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + " -nohwhandshake -- don't use hardware-handshaking\n"+ + "\n"; + static boolean debug = false; + static boolean hwhandshake = false; + + public static void main(String[] args) { + if( args.length < 4 ) { + System.out.println( usage ); + System.exit(0); + } + + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(); + int argOffset = 0; + if (args[1].equals("SCI") || (args[1].equals("OI"))) { + roombacomm.setProtocol(args[1]); + argOffset = 1; + } + + int velocity=0, radius=0, waittime=0; + try { + velocity = Integer.parseInt( args[1+argOffset] ); + radius = Integer.parseInt( args[2+argOffset] ); + waittime = Integer.parseInt( args[3+argOffset] ); + } catch( Exception e ) { + System.err.println("Couldn't parse velocity & radius"); + System.exit(1); + } + + for( int i=4+argOffset; i < args.length; i++ ) { + if( args[i].endsWith("debug") ) + debug = true; + else if( args[i].endsWith("nohwhandshake") ) + roombacomm.setWaitForDSR(false); + else if( args[i].endsWith("hwhandshake") ) + roombacomm.setWaitForDSR(true); + } + + roombacomm.debug = debug; + + if( ! roombacomm.connect( portname ) ) { + System.out.println("Couldn't connect to "+portname); + System.exit(1); + } + + System.out.println("Roomba startup"); + roombacomm.startup(); + roombacomm.control(); + roombacomm.pause(30); + roombacomm.full(); + roombacomm.pause(50); + + int v = velocity; + int r = radius; + int dr = -10; + + boolean done = false; + while( !done ) { + roombacomm.drive( v,r ); + roombacomm.pause( waittime ); + roombacomm.drive( v, (int) r / Math.abs(dr) ); + roombacomm.pause( waittime ); + r += -10; + done = keyIsPressed(); + } + + roombacomm.stop(); + roombacomm.safe(); + System.out.println("Disconnecting"); + roombacomm.disconnect(); + + System.out.println("Done"); + } + + /** check for keypress, return true if so */ + public static boolean keyIsPressed() { + boolean press = false; + try { + if( System.in.available() != 0 ) { + System.out.println("key pressed"); + press = true; + } + } catch( IOException ioe ) { } + return press; + } +} + diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro2.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro2.java new file mode 100644 index 0000000..42a9742 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Spiro2.java @@ -0,0 +1,144 @@ +/* + * roombacomm.Spiro2 -- a Spirograph-like example + * + * 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 com.hackingroomba.roombacomm; + +import java.io.*; + +/** + * A Spirograph-like example + *

+ * Run it with something like:

+ *   java roombacomm.Spiro1 /dev/cu.KeySerial1 velocity radius waittime
+ *  
+ * + */ +public class Spiro2 { + + static String usage = + "Usage: \n"+ + " roombacomm.Spiro2 [options]\n" + + "where [options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + "\n"; + static boolean debug = false; + static boolean hwhandshake = false; + + public static void main(String[] args) { + if( args.length < 6 ) { + System.out.println( usage ); + System.exit(0); + } + + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + int velocity=0, radius=0, waittime=0, waittime2=0, radius2=0; + try { + velocity = Integer.parseInt( args[1] ); + radius = Integer.parseInt( args[2] ); + radius2 = Integer.parseInt( args[3] ); + waittime = Integer.parseInt( args[4] ); + waittime2 = Integer.parseInt( args[5] ); + } catch( Exception e ) { + System.err.println("Couldn't parse arguments"); + System.exit(1); + } + + for( int i=4; i < args.length; i++ ) { + if( args[i].endsWith("debug") ) + debug = true; + else if( args[i].endsWith("hwhandshake") ) + hwhandshake = true; + } + + RoombaCommSerial roombacomm = new RoombaCommSerial(); + roombacomm.debug = debug; + roombacomm.waitForDSR = hwhandshake; + + if( ! roombacomm.connect( portname ) ) { + System.out.println("Couldn't connect to "+portname); + System.exit(1); + } + + System.out.println("Roomba startup"); + roombacomm.startup(); + roombacomm.control(); + roombacomm.pause(30); + roombacomm.full(); + roombacomm.pause(50); + + //int v = velocity; + //int r = radius; + //int dr = dradius; + + int w,dr; + + boolean done = false; + while( !done ) { + roombacomm.drive( velocity,radius ); + //roombacomm.pause( waittime ); + + // lets try some easing + w = waittime / 10; // divide into 10 msec chucks + dr = (radius2 - radius) / 10; + System.out.println("easing "+w+" times at "+dr+" radius"); + for( int i =0; i + Run it with something like:
+    java roombacomm.Spy /dev/cu.KeySerial1
+ Usage: + roombacomm.Spy [protocol] [options]
+ where: protocol (optional) is SCI or OI + [options] can be one or more of: + -pause -- pause n millseconds between sensor read + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -flush -- flush on sends(), normally not needed + -power -- power on/off Roomba (if interface supports it) +
+ */ +public class Spy { + + static String usage = + "Usage: \n"+ + " roombacomm.Spy [protocol] [options]\n" + + "where: protocol (optional) is SCI or OI\n" + + " [options] can be one or more of:\n"+ + " -pause -- pause n millseconds between sensor read\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + " -flush -- flush on sends(), normally not needed\n"+ + " -power -- power on/off Roomba (if interface supports it)\n"+ + "\n"; + + static boolean debug = false; + static boolean hwhandshake = false; + static boolean power = false; + static int pausetime = 500; + + public static void main(String[] args) { + if( args.length == 0 ) { + System.out.println( usage ); + System.exit(0); + } + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(); + + for( int i=1; i + Run it with something like:
+   java roombacomm.SpyAuto /dev/cu.KeySerial1
+  Usage: 
+  roombacomm.SpyAuto serialportname [protocol] [options]
+ where: protocol (optional) is SCI or OI + [options] can be one or more of: + -pause n -- pause n millseconds between sensor read + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -nohwhandshake -- don't use hardware-handshaking + -flush -- flush on sends(), normally not needed + -power -- power on/off Roomba (if interface supports it) +
+ * + */ +public class SpyAuto { + + static String usage = + "Usage: \n"+ + " roombacomm.SpyAuto [protocol] [options]\n" + + "where: protocol (optional) is SCI or OI\n" + + "[options] can be one or more of:\n"+ + " -pause -- pause n millseconds between sensor read\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + " -nohwhandshake -- don't use hardware-handshaking\n"+ + " -flush -- flush on sends(), normally not needed\n"+ + " -power -- power on/off Roomba (if interface supports it)\n"+ + "\n"; + + static boolean debug = false; + static boolean hwhandshake = false; + static boolean power = false; + static int pausetime = 500; + + public static void main(String[] args) { + if( args.length == 0 ) { + System.out.println( usage ); + System.exit(0); + } + + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(true, pausetime); + + for( int i=1; i60; i-- ) { + roombacomm.spinLeftAt( 1000 ); + roombacomm.pause( s64ths/2 ); + roombacomm.spinRightAt( 1000 ); + roombacomm.pause( s64ths/2 ); + roombacomm.stop(); + } + } + + public static void createTribblePurrSong() { + byte cmd[] = { + (byte)RoombaComm.SONG, 2, 7, // define song + 68, 4, 67, 4, 66, 4, 65, 4, + 64, 4, 63, 4, 62, 4 }; + roombacomm.send( cmd ); + } + + + public static void bark() { + System.out.println("bark"); + roombacomm.vacuum(true); + roombacomm.playNote( 50, 5 ); + roombacomm.pause(150); + roombacomm.vacuum(false); + } + */ + diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/SpySimple.java b/roombacomm-client/src/com/hackingroomba/roombacomm/SpySimple.java new file mode 100644 index 0000000..0ef24bc --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/SpySimple.java @@ -0,0 +1,136 @@ +/* + * roombacomm.SpySimple + * + * 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 com.hackingroomba.roombacomm; + +import java.io.*; + +/** + Spy on the Roomba as it goes about its normal business + +

+ Run it with something like:

+    java roombacomm.SpySimple /dev/cu.KeySerial1
+  Usage: 
+  roombacomm.SpySimple serialportname [protocol] [options]
+ where: protocol (optional) is SCI or OI + [options] can be one or more of: + -pause n -- pause n millseconds between sensor read + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -nohwhandshake -- don't use hardware-handshaking + -flush -- flush on sends(), normally not needed *
+ * + */ +public class SpySimple { + + static String usage = + "Usage: \n"+ + " roombacomm.SpySimple [protocol] [options]\n" + + "where: protocol (optional) is SCI or OI\n" + + "[options] can be one or more of:\n"+ + " -pause -- pause n millseconds between sensor read\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + " -nohwhandshake -- don't use hardware-handshaking\n"+ + " -flush -- flush on sends(), normally not needed\n"+ + "\n"; + + static boolean debug = false; + static boolean hwhandshake = false; + static int pausetime = 500; + + public static void main(String[] args) { + if( args.length == 0 ) { + System.out.println( usage ); + System.exit(0); + } + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(); + + for( int i=1; i + Run it with something like:
+    java roombacomm.Test /dev/cu.KeySerial1
+  Usage: 
+  roombacomm.Test serialportname [protocol] [options]
+ where: protocol (optional) is SCI or OI + [options] can be one or more of: + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -nohwhandshake -- don't use hardware-handshaking + -flush -- flush on sends(), normally not needed + -power -- power on/off Roomba (if interface supports it) + *
+ *

+ */ +public class Test { + + static String usage = + "Usage: \n"+ + " roombacomm.Test [protocol] [options]\n" + + "where: protocol (optional) is SCI or OI\n" + + "[options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + " -nohwhandshake -- don't use hardware-handshaking\n"+ + " -flush -- flush on sends(), normally not needed\n"+ + " -power -- power on/off Roomba (if interface supports it)\n"+ + "\n"; + + static boolean debug = false; + static boolean hwhandshake = false; + static boolean power = false; + static boolean flush = false; + + public static void main(String[] args) { + if( args.length == 0 ) { + System.out.println(usage); + System.exit(0); + } + + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(); + + for( int i=1; i + * Run it with something like:
+ *   java roombacomm.Tribble /dev/cu.KeySerial1 [protocol]
+ * Where: protocol (optional) is SCI or OI + *
+ * + */ +public class Tribble { + + static RoombaCommSerial roombacomm; + + public static void main(String[] args) { + new Tribble(args); + } + + Tribble(String[] args) + { + if( args.length == 0 ) { + System.out.println("Tribble [protocol]\nWhere: protocol (optional) is SCI or OI"); + System.exit(0); + } + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + + roombacomm = new RoombaCommSerial(); + for( int i=1; i < args.length; i++ ) { + if (args[i].equals("SCI") || (args[1].equals("OI"))) { + roombacomm.setProtocol(args[i]); + } + } + + if( ! roombacomm.connect( portname ) ) { + System.err.println("Couldn't connect to "+portname); + System.exit(1); + } + + System.err.println("Roomba startup"); + roombacomm.startup(); + roombacomm.control(); + roombacomm.pause(100); + + createTribblePurrSong(); + + System.out.println("Press return to exit."); + boolean done = false; + while( !done ) { + + purr(); + + if( Math.random() < 0.1 ) + bark(); + + roombacomm.pause(1500 + (int)(Math.random()*500) ); + done = keyIsPressed(); + } + + roombacomm.disconnect(); + System.exit(0); + } + + public static void purr() { + System.out.println("purr"); + roombacomm.playSong( 5 ); + for( int i=0; i<5; i++ ) { + roombacomm.spinLeftAt( 75 ); + roombacomm.pause( 100 ); + roombacomm.spinRightAt( 75 ); + roombacomm.pause( 100 ); + roombacomm.stop(); + } + } + + public static void createTribblePurrSong() { + int song[] = + { 68,4, 67,4, 66,4, 65,4, + 64,4, 63,4, 62,4, 61,4, + 60,4, 59,4, 60,4, 61,4, + }; + roombacomm.createSong( 5, song ); + } + + public static void bark() { + System.out.println("bark"); + roombacomm.vacuum(true); + roombacomm.playNote( 50, 5 ); + roombacomm.pause(150); + roombacomm.vacuum(false); + } + + + /** check for keypress, return true if so */ + public static boolean keyIsPressed() { + boolean press = false; + try { + if( System.in.available() != 0 ) { + System.out.println("key pressed"); + press = true; + } + } catch( IOException ioe ) { } + return press; + } + +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/Waggle.java b/roombacomm-client/src/com/hackingroomba/roombacomm/Waggle.java new file mode 100644 index 0000000..28507b2 --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/Waggle.java @@ -0,0 +1,115 @@ +/* + * roombacomm.Waggle -- test out the DRIVE command, showing a waggle + * + * 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 com.hackingroomba.roombacomm; + +/** + Drive the Roomba in a Waggle, like when it's searching for something +

+ Run it with something like:

+   java roombacomm.Waggle /dev/cu.KeySerial1 [protocol] velocity radius waittime [options]
+  where: 
+  protocol (optional) is SCI or OI
+  velocity and radius in mm, waittime in milliseconds
+  [options] can be one or more of:
+   -debug       -- turn on debug output
+   -hwhandshake -- use hardware-handshaking, for Windows Bluetooth
+   -nohwhandshake -- don't use hardware-handshaking
+ *  
+ * + */ +public class Waggle { + + static String usage = + "Usage: \n"+ + " roombacomm.Waggle [protocol] [options]\n" + + "where: protocol (optional) is SCI or OI\n" + + "[options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + " -nohwhandshake -- don't use hardware-handshaking\n"+ + "\n"; + static boolean debug = false; + static boolean hwhandshake = false; + + public static void main(String[] args) { + if( args.length < 4 ) { + System.out.println( usage ); + System.exit(0); + } + + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + RoombaCommSerial roombacomm = new RoombaCommSerial(); + int argOffset = 0; + if (args[1].equals("SCI") || (args[1].equals("OI"))) { + roombacomm.setProtocol(args[1]); + argOffset = 1; + } + + int velocity=0, radius=0, waittime=0; + try { + velocity = Integer.parseInt( args[1+argOffset] ); + radius = Integer.parseInt( args[2+argOffset] ); + waittime = Integer.parseInt( args[3+argOffset] ); + } catch( Exception e ) { + System.err.println("Couldn't parse velocity or radius or waittime"); + System.exit(1); + } + + for( int i=4+argOffset; i < args.length; i++ ) { + if( args[i].endsWith("debug") ) + debug = true; + else if( args[i].endsWith("nohwhandshake") ) + roombacomm.setWaitForDSR(false); + else if( args[i].endsWith("hwhandshake") ) + roombacomm.setWaitForDSR(true); + } + + roombacomm.debug = debug; + + if( ! roombacomm.connect( portname ) ) { + System.out.println("Couldn't connect to "+portname); + System.exit(1); + } + + System.out.println("Roomba startup"); + roombacomm.startup(); + roombacomm.control(); + roombacomm.pause(100); + + System.out.println("waggling 5 times\n"); + for( int i=0; i<5; i++ ) { + roombacomm.drive( velocity, radius ); + roombacomm.pause(waittime); + roombacomm.drive( velocity, -radius ); + roombacomm.pause(waittime); + } + roombacomm.stop(); + + System.out.println("Disconnecting"); + roombacomm.disconnect(); + + System.out.println("Done"); + } + +} + diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/XML.java b/roombacomm-client/src/com/hackingroomba/roombacomm/XML.java new file mode 100644 index 0000000..e64ce0f --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/XML.java @@ -0,0 +1,173 @@ +package com.hackingroomba.roombacomm; + +import java.util.Hashtable; +import java.util.Vector; + +/* The following XML class is a simple XML class meant to process the primitive XML + * that comes from RoboRealm. This class can be removed and replaced with a more + * extensive XML processing class as needed but is guaranteed to work with the RR + * XML. Do NOT use this class for generic XML processing as it is included for + * completeness and is intentionally kept simplistic to ease understanding + * */ + +class XML +{ + Hashtable table = new Hashtable(); + Vector list = new Vector(); + + private String replace(String txt, String src, String dest) + { + if (txt==null) return new String(""); + int i,j; + int len=src.length(); + StringBuffer sb=new StringBuffer(txt.length()); + + j=0; + while ((i=txt.indexOf(src,j))>=0) + { + sb.append(txt.substring(j,i)); + sb.append(dest); + i+=len; + j=i; + } + sb.append(txt.substring(j)); + + return sb.toString(); + } + + /* + Unescapes strings that have been included in an XML message. This can be + accomplished by a sequence of replace statements. + & -> & + "e; -> " + < -> < + > -> > + */ + private String unescape(String txt) + { + replace(txt, "&", "&"); + replace(txt, ""e;", "\""); + replace(txt, "<", "<"); + replace(txt, ">", ">"); + return txt; + } + + public boolean parse(String s) + { + table.clear(); + return parse(s, table, null); + } + + public Vector parseVector(String s) + { + list.removeAllElements(); + if (parse(s, null, list)) + return list; + else + return null; + } + + public boolean parse(String s, Hashtable h, Vector v) + { + boolean isEndTag; + byte txt[] = s.getBytes(); + int i, j; + int len = s.length(); + StringBuffer keys[] = new StringBuffer[10]; + StringBuffer value = new StringBuffer(); + for (i=0;i<10;i++) + keys[i] = new StringBuffer(); + int keyTop=-1; + + for (i=0;i tag"); + return false; + } + + if (isEndTag) + { + if (!keys[keyTop].toString().equals(keys[keyTop-1].toString())) + { + System.out.println("Mismatched XML tags "+keys[keyTop]+" -> "+keys[keyTop-1]); + return false; + } + keyTop-=2; + } + } + else + { + // read in value + value.setLength(0); + + while ((i0) key.append('.'); + key.append(keys[j]); + } + + String escapedValue = unescape(value.toString()); + if (h!=null) h.put(key.toString(), escapedValue); + if (v!=null) v.addElement(escapedValue); + } + } + + return true; + } + + public int getInt(String txt) + { + String s = (String)table.get(txt); + if (s!=null) + { + return Integer.parseInt(s); + } + return 0; + } + + public String getFirst() + { + if (table.isEmpty()) + return null; + else + return (String)table.elements().nextElement(); + } +/* + // This is where the program first starts + public static void main(String[] args) + { + XML xml = new XML(); + xml.parse("100200"); + System.out.println(xml.getInt("response.width")); + System.out.println(xml.getInt("response.height")); + } +*/ +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_backward.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_backward.png new file mode 100644 index 0000000..aa0c5d2 Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_backward.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_checkrobotOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_checkrobotOn.png new file mode 100644 index 0000000..4032a78 Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_checkrobotOn.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_cleanOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_cleanOn.png new file mode 100644 index 0000000..49328a9 Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_cleanOn.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dirtOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dirtOn.png new file mode 100644 index 0000000..72c3c03 Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dirtOn.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dockOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dockOn.png new file mode 100644 index 0000000..8866a47 Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_dockOn.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_forward.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_forward.png new file mode 100644 index 0000000..b11d65b Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_forward.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_maxOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_maxOn.png new file mode 100644 index 0000000..4719cc6 Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_maxOn.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinleft.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinleft.png new file mode 100644 index 0000000..4bdd361 Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinleft.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinright.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinright.png new file mode 100644 index 0000000..c7a5c0d Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spinright.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spotOn.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spotOn.png new file mode 100644 index 0000000..af1e18d Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_spotOn.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_stop.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_stop.png new file mode 100644 index 0000000..a78b6dc Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_stop.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_play.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_play.png new file mode 100644 index 0000000..48e1159 Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_play.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_record.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_record.png new file mode 100644 index 0000000..0333e02 Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_record.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_stop.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_stop.png new file mode 100644 index 0000000..3dddea3 Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_transport_stop.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnleft.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnleft.png new file mode 100644 index 0000000..f47c7ff Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnleft.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnright.png b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnright.png new file mode 100644 index 0000000..b554358 Binary files /dev/null and b/roombacomm-client/src/com/hackingroomba/roombacomm/images/but_turnright.png differ diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/net/DumpMethods.java b/roombacomm-client/src/com/hackingroomba/roombacomm/net/DumpMethods.java new file mode 100644 index 0000000..962629e --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/net/DumpMethods.java @@ -0,0 +1,70 @@ + +package com.hackingroomba.roombacomm.net; + +import java.lang.reflect.*; + +import com.hackingroomba.roombacomm.*; + + +public class DumpMethods { + + RoombaCommSerial rcs; + + public static void main(String[] args) { + new DumpMethods(args); + } + + public DumpMethods(String[] args) { + rcs = new RoombaCommSerial(); + + if( args.length == 0 ) { + dumpMethods(rcs); + return; + } + + String method_name = args[0]; + + /* + for( int i=1; i + * Run it with something like:
+ *   java roombacomm.SimpleTest /dev/cu.KeySerial1
+ *  
+ * + */ +public class SimpleTest { + + static String usage = + "Usage: \n"+ + " roombacomm.net.SimpleTest [options]\n" + + "where [options] can be one or more of:\n"+ + " -debug -- turn on debug output\n"+ + //" -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"+ + //" -flush -- flush on sends(), normally not needed\n"+ + "\n"; + static boolean debug = false; + static boolean hwhandshake = false; + static boolean flush = false; + + public static void main(String[] args) { + if( args.length == 0 ) { + System.out.println( usage ); + System.exit(0); + } + + String portname = args[0]; // e.g. "/dev/cu.KeySerial1" + + for( int i=1; i < args.length; i++ ) { + if( args[i].endsWith("debug") ) + debug = true; + //else if( args[i].endsWith("hwhandshake") ) + // hwhandshake = true; + //else if( args[i].endsWith("flush") ) + // flush = true; + } + + RoombaComm roombacomm = new RoombaCommTCPClient(); + + roombacomm.debug = debug; + //roombacomm.waitForDSR = hwhandshake; + //roombacomm.flushOutput = flush; + + String portlist[] = roombacomm.listPorts(); + System.out.println("Available ports:"); + for(int i=0;i index1) + return requestString.substring(index1 + 1, index2); + } + return null; + } + + +} diff --git a/roombacomm-client/src/com/hackingroomba/roombacomm/watchVideo.java b/roombacomm-client/src/com/hackingroomba/roombacomm/watchVideo.java new file mode 100644 index 0000000..978459a --- /dev/null +++ b/roombacomm-client/src/com/hackingroomba/roombacomm/watchVideo.java @@ -0,0 +1,255 @@ +/* + * roombacomm.WatchVideo -- test out the video subsystem without robot motion + * + * Copyright (c) 2009 Paul Bouchier, bouchier@at@classicnet.net + * + * 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 com.hackingroomba.roombacomm; + +import jargs.gnu.CmdLineParser; +import java.io.*; + + +/** + Run it with something like:
+	    java roombacomm.WatchVideo --videoServer 192.168.0.150 --videoPortNum 5005 -x 160 -y 120 --debug
+ Usage: + roombacomm.WatchVideo --videoServer --videoPortNum + -x -y [options]
+ where + [options] can be one or more of: + -debug -- turn on debug output + -hwhandshake -- use hardware-handshaking, for Windows Bluetooth + -nohwhandshake -- don't use hardware-handshaking +
+*/ + public class watchVideo { + + private int height = 120; + private int width = 160; + String usage = + "Usage: \n"+ + " roombacomm.Roborama --videoServer --videoPortNum -x -y [options]\n" + + "where [options] can be one or more of:\n"+ + " -X | --debug -- turn on debug output\n"+ + " -x or --width : set width\n" + + " -y or --height -- set height\n" + + " -c | --color -- use color mode, otherwise grayscale\n" + + " --videoserver : set IP address of video server\n" + + " --videoPortNum : set port number on video server\n" + + " -t | --threshold : set quantization threshold\n" + + " -R | --RoboRealm : connect to Roborealm & send image to it\n" + + " -hwhandshake -- use hardware-handshaking, for Windows Bluetooth\n"; + + boolean debug = false; + private int waittime; + private int thresholdOverride = 0; + private String videoServer = ""; + private int videoPortNum = 5005 ; + private int videoRowStart = 50; + private int videoRowEnd = 54; + private int pixelCnt; + private boolean color = false; // when true, capture images in color + private boolean roborealm = false; // when true, connect & send images to RoboRealm + FrameProcessor fp; + + public watchVideo() { + // constructor, mustn't throw exceptions. Do nothing for now + } + + // main - it all starts here + public static void main(String[] args) { + watchVideo b = new watchVideo(); + b.parseCmd(args); + b.initVideo(); + b.displayVideo(); + } + + public void displayVideo() + { + + while (true) { + getShowFrame(); + if (roborealm) { + Boolean rv = fp.frame2Roborealm(); + if (rv == false) + System.out.println("error sending image to Roborealm"); + //fp.getShapeData(); + } + } + //frameSize = fp.readFrame(); + //System.out.println("getVideo read " + frameSize + " bytes"); + //fp.disconnect(); + } + + /** + * Get a frame from the video server and display it + */ + public void getShowFrame() { + pixelCnt = fp.readFrame(getWidth(), getHeight(), color?1:0); + if (pixelCnt != (getWidth() * getHeight())) { + System.out.println("getVideo read " + pixelCnt + " bytes - abandoning frame"); + return; + } + + // output the frame to wherever it's wanted + fp.displayFrame(); + return; + } + + /** + * Connect to the video server, and to RoboRealm if requested + */ + public FrameProcessor initVideo() { + if (getVideoServer() == null || getVideoServer().length() == 0) { + System.err.println(" you must supply a --videoServer value to use the command \"getVideo\""); + if (getVideoPortNum() <= 0) { + System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\""); + } + System.exit(6); + } + if (getVideoPortNum() <= 0) { + System.err.println(" you must supply a --videoPortNum value to use the command \"getVideo\""); + System.exit(7); + } + + fp = new FrameProcessor(getVideoServer(),getVideoPortNum(), 0, 0, 0, 0, getThresholdOverride()); + //fp.testQuantization(); + fp.createAndShowGUI(); + fp.connect(roborealm); // connect to the video server, and to Roborealm too if requested + + return fp; + } + + public void parseCmd(String[] args){ + System.out.println("*** start of WatchVideo WparseCmd"); + + CmdLineParser parser = new CmdLineParser(); + CmdLineParser.Option debugOption = parser.addBooleanOption('X', "debug"); +// CmdLineParser.Option verboseOption = parser.addBooleanOption('W', "Verbose"); + CmdLineParser.Option widthOption = parser.addIntegerOption('x', "width"); + CmdLineParser.Option heightOption = parser.addIntegerOption('y', "height"); + CmdLineParser.Option videoServerOption = parser.addStringOption("videoServer"); + CmdLineParser.Option videoPortNumOption = parser.addIntegerOption("videoPortNum"); + CmdLineParser.Option thresholdOption = parser.addIntegerOption('t', "threshold"); + CmdLineParser.Option hwHandShakeOption = parser.addBooleanOption("nohwhandshake"); + CmdLineParser.Option colorOption = parser.addBooleanOption('c', "color"); + CmdLineParser.Option roborealmOption = parser.addBooleanOption('R', "roborealm"); + try { + parser.parse(args); + } + catch ( CmdLineParser.OptionException e ) { + System.err.println(e.getMessage()); + System.out.println("parseCmd had an error\n"+ usage ); + System.exit(2); + } + + // String portname = args[0]; // e.g. "/dev/cu.KeySerial1", or "COM5" or "192.168.1.1" + setThresholdOverride(((Integer)parser.getOptionValue(thresholdOption, getThresholdOverride())).intValue()); + setWidth(((Integer)parser.getOptionValue(widthOption,getWidth())).intValue()); + setHeight(((Integer)parser.getOptionValue(heightOption,getHeight())).intValue()); + setVideoServer(((String)parser.getOptionValue(videoServerOption))); + setVideoPortNum(((Integer)parser.getOptionValue(videoPortNumOption, getVideoPortNum())).intValue()); + // String cmd = args[1+argOffset]; + + Boolean debugBool = (Boolean)parser.getOptionValue(debugOption,new Boolean(false)); + setDebug(debugBool.booleanValue()); + System.out.println("debug is ("+isDebug()+")"); + Boolean hwHandShakeBool = (Boolean)parser.getOptionValue(hwHandShakeOption, new Boolean(false)); + setThresholdOverride((Integer)parser.getOptionValue(thresholdOption, new Integer(0))); + System.out.println("thresholdOverride is " + getThresholdOverride()); + color = (Boolean)parser.getOptionValue(colorOption, new Boolean(false)); + roborealm = (Boolean)parser.getOptionValue(roborealmOption, new Boolean(false)); + System.out.println("color mode is " + color + ", roborealm mode is " + roborealm); + System.out.println("*** end of parseCmd"); + } + + public boolean isDebug() { + return debug; + } + public void setDebug(boolean debug_) { + debug = debug_; + } + public int getWaittime() { + return waittime; + } + public void setWaittime(int waittime_) { + waittime = waittime_; + } + public int getThresholdOverride() { + return thresholdOverride; + } + public void setThresholdOverride(int thresholdOverride) { + this.thresholdOverride = thresholdOverride; + } + protected int getHeight() { + return height; + } + protected void setHeight(int height) { + this.height = height; + } + protected int getWidth() { + return width; + } + protected void setWidth(int width) { + this.width = width; + } + /** + * @return the videoServer + */ + protected String getVideoServer() { + return videoServer; + } + /** + * @param videoServer the videoServer to set + */ + protected void setVideoServer(String videoServer) { + this.videoServer = videoServer; + } + /** + * @return the videoPortNum + */ + protected int getVideoPortNum() { + return videoPortNum; + } + /** + * @param videoPortNum the videoPortNum to set + */ + protected void setVideoPortNum(int videoPortNum) { + this.videoPortNum = videoPortNum; + } + + public boolean isColor() { + return color; + } + + public void setColor(boolean color) { + this.color = color; + } + + public boolean isRoborealm() { + return roborealm; + } + + public void setRoborealm(boolean roborealm) { + this.roborealm = roborealm; + } + + } + + \ No newline at end of file -- cgit v1.3.1