Пример #1
0
  public static void main(String[] args) {

    if (args.length != 1) {
      System.out.println("usage java TestSer port");
      System.exit(-1);
    }
    TestSer ts = new TestSer(args[0]);

    byte[] buf = new byte[256];

    ts.start();

    start = System.currentTimeMillis();

    new Thread() {
      public void run() {

        for (; ; ) {
          try {
            Thread.sleep(1000);
          } catch (Exception e) {
          }

          long i = cntRcv;
          long t = System.currentTimeMillis() - start;

          if (t != 0) {
            System.out.print(
                (cntSend / 1024)
                    + " KB sent "
                    + (i / 1024)
                    + " KB received "
                    + (cntSend / 1024 * 1000 / t)
                    + " KB/s "
                    + (i / 1024 * 1000 / t)
                    + " KB/s   \r");
          }
        }
      }
    }.start();

    for (; ; ) {

      try {
        os.write(buf);
      } catch (Exception e) {
        System.out.println(e);
      }
      cntSend += buf.length;
    }
  }
Пример #2
0
  Server() throws IOException {
    if (useSerial) {
      ServerThread.initSerial();
      ServerThread.writeSerial(true);
      try {
        Thread.sleep(200);
      } catch (InterruptedException e1) {
      }
      ServerThread.writeSerial(true);
      try {
        Thread.sleep(200);
      } catch (InterruptedException e1) {
      }
      ServerThread.writeSerial(true);
    }

    int port = 10055;
    ServerSocket serverSocket = null;
    try {
      serverSocket = new ServerSocket(port);
      System.out.println("Soccer robots server active on port " + port + ".");
    } catch (IOException e) {
      System.err.println("Could not listen on port: " + port + ".");
      System.exit(-1);
    }
    while (true) {
      ServerThread tmp = new ServerThread(serverSocket.accept());
      threads.add(tmp);
    }
  }
  public boolean avrdude(Collection params) throws RunnerException {
    List commandDownloader = new ArrayList();
    commandDownloader.add("avrdude");

    // Point avrdude at its config file since it's in a non-standard location.
    if (Base.isMacOS()) {
      commandDownloader.add("-C" + "hardware/tools/avr/etc/avrdude.conf");
    } else if (Base.isWindows()) {
      String userdir = System.getProperty("user.dir") + File.separator;
      commandDownloader.add("-C" + userdir + "hardware/tools/avr/etc/avrdude.conf");
    } else {
      // ???: is it better to have Linux users install avrdude themselves, in
      // a way that it can find its own configuration file?
      commandDownloader.add("-C" + "hardware/tools/avrdude.conf");
    }

    if (Preferences.getBoolean("upload.verbose")) {
      commandDownloader.add("-v");
      commandDownloader.add("-v");
      commandDownloader.add("-v");
      commandDownloader.add("-v");
    } else {
      commandDownloader.add("-q");
      commandDownloader.add("-q");
    }
    // XXX: quick hack to chop the "atmega" off of "atmega8" and "atmega168",
    // then shove an "m" at the beginning.  won't work for attiny's, etc.
    commandDownloader.add(
        "-pm" + Preferences.get("boards." + Preferences.get("board") + ".build.mcu").substring(6));
    commandDownloader.addAll(params);

    return executeUploadCommand(commandDownloader);
  }
Пример #4
0
  public TestSer(String port) {

    try {
      portId = CommPortIdentifier.getPortIdentifier(port);
      serialPort = (SerialPort) portId.open("TestSer", 2000);
      is = serialPort.getInputStream();
      os = serialPort.getOutputStream();
      /*
      			serialPort.addEventListener(this);
      			serialPort.notifyOnDataAvailable(true);
      */

      serialPort.setSerialPortParams(
          115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

      serialPort.setFlowControlMode(
          SerialPort.FLOWCONTROL_RTSCTS_OUT | SerialPort.FLOWCONTROL_RTSCTS_IN);

      serialPort.enableReceiveTimeout(TIMEOUT);
      //			serialPort.enableReceiveThreshold(4);

    } catch (Exception e) {
      System.out.println(e.getMessage());
      System.exit(-1);
    }
  }
Пример #5
0
  public static Serial open(String defaultPort) {
    boolean portFound = false;
    final int mComPortIdentifier = CommPortIdentifier.PORT_SERIAL;
    final Enumeration portList = CommPortIdentifier.getPortIdentifiers();
    final int BAUD = 115200;

    while (portList.hasMoreElements()) {
      final CommPortIdentifier portId = (CommPortIdentifier) portList.nextElement();
      System.out.println("Found port id: " + portId);

      if (portId.getPortType() == mComPortIdentifier) {
        System.out.println("Found CommPortIdentifier.");

        if (portId.getName().equals(defaultPort)) {
          System.out.println("Found port " + defaultPort);

          SerialPort serialPort;
          OutputStream outputStream = null;

          try {
            serialPort = (SerialPort) portId.open("SimpleWrite", 2000);
          } catch (PortInUseException e) {
            System.out.println("Port in use.");
            continue;
          }

          try {
            outputStream = serialPort.getOutputStream();
          } catch (IOException e) {
            e.printStackTrace();
          }

          try {
            serialPort.setSerialPortParams(
                BAUD, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
          } catch (UnsupportedCommOperationException e) {
            e.printStackTrace();
          }

          try {
            serialPort.notifyOnOutputEmpty(true);
          } catch (Exception e) {
            System.out.println("Error setting event notification");
            System.out.println(e.toString());
            System.exit(-1);
          }

          return new Serial(serialPort, outputStream);
        }
      }
    }

    if (!portFound) {
      System.out.println("port " + defaultPort + " not found.");
    }
    return null;
  }
 private void sendData(byte[] data) throws SerialConnectionException {
   try {
     int size = data.length;
     int sent = 0;
     _sent = 0;
     long starttime = System.currentTimeMillis();
     while (sent < size) {
       int tosend;
       if (!_keepRunning) return;
       if ((sent + _chunkSize) > size) {
         tosend = size - sent;
       } else {
         tosend = _chunkSize;
       }
       _os.write(data, sent, tosend);
       sent += tosend;
       _sent = sent;
       // update GUI
       _results.updateBytesSent(sent);
     }
   } catch (IOException e) {
     throw new SerialConnectionException("Error writing to i/o streams");
   }
 }
Пример #7
0
class ServerThread extends Thread implements constants {
  static Team[] teams = new Team[2];
  DataInputStream in;

  ServerThread(Socket socket) {
    try {
      in = new DataInputStream(socket.getInputStream());
    } catch (IOException e) {
      e.printStackTrace();
    }
    teams[0] = new KairusTeam(0);
    teams[1] = new EmptyTeam(1);
    this.start();
    new gameRunner(new Team[] {teams[0], teams[1]}).start();
  }

  long lastTime = System.currentTimeMillis();
  double[][][] robotVelocitys = new double[2][5][3];
  double[][][] robotPositions = new double[2][5][3];
  double[] ballPosition = new double[2];
  double[] ballVelocity = new double[2];

  public double toBigEndian(double d) {
    ByteBuffer bb = ByteBuffer.wrap(new byte[8]);
    bb.putDouble(d);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    bb.position(0);
    return bb.getDouble();
  }

  public double toRadians(double d) {
    return (d * Math.PI) / 180;
  }

  static DataOutputStream comOS = null;

  public static void initSerial() {
    Enumeration portList = CommPortIdentifier.getPortIdentifiers();
    CommPortIdentifier portId = null;
    boolean portFound = false;
    while (portList.hasMoreElements()) {
      portId = (CommPortIdentifier) portList.nextElement();
      if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
        if (portId.getName().equals("COM3")) {
          portFound = true;
        }
      }
    }
    if (!portFound) {
      System.out.println("port COM3 not found.");
      return;
    }
    SerialPort port;
    try {
      port = (SerialPort) portId.open("COM3", 2000);
      port.setSerialPortParams(
          115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
      comOS = new DataOutputStream(port.getOutputStream());
    } catch (PortInUseException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } catch (UnsupportedCommOperationException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  static int[] inputs = new int[10];

  public static void writeSerial(boolean stop) {
    byte[] toSend = new byte[48];
    toSend[0] = (byte) 0xff;
    toSend[1] = (byte) 0xff;
    toSend[2] = (byte) 0x01;

    int MAX_SPEED = 8196; // 8196
    int accelerationJump = 30;

    if (!stop) {

      for (int i = 0; i < 5; i++) {
        // if (i == 0)
        //	System.out.println("speed: "+teams[0].robot[i].GetSpeedInput()+" spin:
        // "+teams[0].robot[i].GetSpinInput());
        inputs[i * 2] = (int) (teams[0].robot[i].GetSpeedInput() * MAX_SPEED * -1);
        inputs[i * 2 + 1] = (int) (teams[0].robot[i].GetSpinInput() * MAX_SPEED * -1);
        // if (i == 0)
        //	System.out.println("new speed: "+inputs[i*2]+" spin: "+inputs[i*2+1]);
      }

      for (int i = 0; i < 10; i++) {
        if (inputs[i] > MAX_SPEED) inputs[i] = MAX_SPEED;
        if (inputs[i] < -MAX_SPEED) inputs[i] = -MAX_SPEED;
        toSend[2 * i + 0 + 3] = (byte) ((inputs[i]) & 0xff);
        toSend[2 * i + 1 + 3] = (byte) ((inputs[i] >> 8) & 0xff);
      }
    }
    /*
    for (int team = 0;team<1;team++)
    	for (int i = 0;i<5;i++){
    		int linV = (int) (teams[team].robot[i].GetSpeedInput()*MAX_SPEED);
    		int angV = (int) (teams[team].robot[i].GetSpinInput() *MAX_SPEED);

    		toSend[team*20+ 4*i+0+3] = (byte)( (linV   ) & 0xff);
    		toSend[team*20+ 4*i+1+3] = (byte)( (linV>>8) & 0xff);
    		toSend[team*20+ 4*i+2+3] = (byte)( (angV   ) & 0xff);
    		toSend[team*20+ 4*i+3+3] = (byte)( (angV>>8) & 0xff);
    	}
    */
    byte checksum = 0;
    for (int i = 0; i < 47; i++) {
      checksum += toSend[i];
    }
    toSend[47] = (byte) -checksum;

    try {
      comOS.write(toSend);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  int upto = 0;

  float goalX1 = 0.001f;
  float wallX1 = 0.095f;
  float wallX2 = 2.111f;
  float goalX2 = 2.208f;

  float Xoffset = -wallX1;
  float Xmultiplier = (FIELD_WIDTH / 100) / (wallX2 - wallX1);

  public void run() {
    try {
      Thread.sleep(1000);
      // writeSerial(false);
      // Thread.sleep(1000);
      // writeSerial(true);
      // System.exit(0);

      while (true) {

        long delta = System.currentTimeMillis() - lastTime;
        lastTime = System.currentTimeMillis();
        double multiplier = (delta == 0 ? (1000 / 1) : (1000 / delta)) / SCALE;
        for (int team = 0; team < 2; team++)
          for (int i = 0; i < 5; i++) {
            // System.out.print("[ ");
            for (int o = 0; o < 3; o++) {
              double read = in.readDouble();
              // System.out.print(o+" Read: "+read);
              double newValue = toBigEndian(read);
              // System.out.print(" Converted: "+newValue);
              if (o == 0) { // Convert X
                newValue += Xoffset;
                newValue *= Xmultiplier;
              }
              if (o == 1) { // Flip Y
                newValue = (1.8 - newValue);
              }
              if (o == 2) { // Convert angle
                newValue = -1 * toRadians(newValue);
              }
              robotVelocitys[team][i][o] = (newValue - robotPositions[team][i][o]) * multiplier;
              robotPositions[team][i][o] = newValue;
              // System.out.print(" "+newValue+" ("+robotVelocitys[team][i][o]+") ");

              /*
              if (team == 0 && i == 0){
              	System.out.print(newValue+" ");
              	if (o == 2)
              		System.out.println();
              }*/

              // System.out.println();

            }
            // System.out.print("] ");
          }

        // Get the ball
        for (int i = 0; i < 2; i++) {
          double newValue = toBigEndian(in.readDouble());
          if (i == 0) { // Convert X
            newValue += Xoffset;
            newValue *= Xmultiplier;
          }
          if (i == 1) { // Flip Y
            newValue = 1.8 - newValue;
          }
          ballVelocity[i] = (newValue - ballPosition[i]) * multiplier;
          ballPosition[i] = newValue;
        }

        // Put unknown positions (enemies) at -10000
        for (int i = 0; i < 2; i++)
          for (int o = 0; o < 5; o++)
            for (int p = 0; p < 2; p++) {
              if (robotPositions[i][o][p] < -1 || robotPositions[i][o][p] > 2.6) {
                System.out.println(
                    i + " " + o + " " + p + " Out of bounds! " + robotPositions[i][o][p]);
                robotPositions[i][o][p] = -10000;
              }
            }

        // Set frame data!
        for (int i = 0; i < 2; i++)
          for (int o = 0; o < 5; o++) {
            teams[i].robot[o].body.setType(BodyType.STATIC);
            teams[i].robot[o].body.setTransform(
                new Vec2(
                    (float) robotPositions[i][o][0] * 100, (float) robotPositions[i][o][1] * 100),
                (float) (robotPositions[i][o][2]));
            // System.out.println("Setting to "+(float)robotPositions[i][o][0]*100+"
            // "+(float)robotPositions[i][o][1]*100);
            teams[i].robot[o].body.setType(BodyType.DYNAMIC);
            teams[i].robot[o].body.setLinearVelocity(
                new Vec2(
                    (float) robotVelocitys[i][o][0] * 100, (float) robotVelocitys[i][o][1] * 100));
            teams[i].robot[o].body.setAngularVelocity((float) robotVelocitys[i][o][2]);
          }
        teams[1].ball.setType(BodyType.STATIC);
        teams[1].ball.setTransform(
            new Vec2((float) ballPosition[0] * 100, (float) ballPosition[1] * 100), 0);
        teams[1].ball.setType(BodyType.DYNAMIC);
        teams[1].ball.setLinearVelocity(
            new Vec2((float) ballVelocity[0] * 100, (float) ballVelocity[1] * 100));
        teams[1].ball.setAngularVelocity(0); // Not sent, reset it!

        // Simulate a frame!
        for (int i = 0; i < 2; i++) teams[i].gameFrame();

        // Send information to serial port!

        if (Server.useSerial) writeSerial(false);
      }
    } catch (IOException e) {
      Server.threads.remove(this);
      e.printStackTrace();
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
Пример #8
0
  public void run() {
    try {
      Thread.sleep(1000);
      // writeSerial(false);
      // Thread.sleep(1000);
      // writeSerial(true);
      // System.exit(0);

      while (true) {

        long delta = System.currentTimeMillis() - lastTime;
        lastTime = System.currentTimeMillis();
        double multiplier = (delta == 0 ? (1000 / 1) : (1000 / delta)) / SCALE;
        for (int team = 0; team < 2; team++)
          for (int i = 0; i < 5; i++) {
            // System.out.print("[ ");
            for (int o = 0; o < 3; o++) {
              double read = in.readDouble();
              // System.out.print(o+" Read: "+read);
              double newValue = toBigEndian(read);
              // System.out.print(" Converted: "+newValue);
              if (o == 0) { // Convert X
                newValue += Xoffset;
                newValue *= Xmultiplier;
              }
              if (o == 1) { // Flip Y
                newValue = (1.8 - newValue);
              }
              if (o == 2) { // Convert angle
                newValue = -1 * toRadians(newValue);
              }
              robotVelocitys[team][i][o] = (newValue - robotPositions[team][i][o]) * multiplier;
              robotPositions[team][i][o] = newValue;
              // System.out.print(" "+newValue+" ("+robotVelocitys[team][i][o]+") ");

              /*
              if (team == 0 && i == 0){
              	System.out.print(newValue+" ");
              	if (o == 2)
              		System.out.println();
              }*/

              // System.out.println();

            }
            // System.out.print("] ");
          }

        // Get the ball
        for (int i = 0; i < 2; i++) {
          double newValue = toBigEndian(in.readDouble());
          if (i == 0) { // Convert X
            newValue += Xoffset;
            newValue *= Xmultiplier;
          }
          if (i == 1) { // Flip Y
            newValue = 1.8 - newValue;
          }
          ballVelocity[i] = (newValue - ballPosition[i]) * multiplier;
          ballPosition[i] = newValue;
        }

        // Put unknown positions (enemies) at -10000
        for (int i = 0; i < 2; i++)
          for (int o = 0; o < 5; o++)
            for (int p = 0; p < 2; p++) {
              if (robotPositions[i][o][p] < -1 || robotPositions[i][o][p] > 2.6) {
                System.out.println(
                    i + " " + o + " " + p + " Out of bounds! " + robotPositions[i][o][p]);
                robotPositions[i][o][p] = -10000;
              }
            }

        // Set frame data!
        for (int i = 0; i < 2; i++)
          for (int o = 0; o < 5; o++) {
            teams[i].robot[o].body.setType(BodyType.STATIC);
            teams[i].robot[o].body.setTransform(
                new Vec2(
                    (float) robotPositions[i][o][0] * 100, (float) robotPositions[i][o][1] * 100),
                (float) (robotPositions[i][o][2]));
            // System.out.println("Setting to "+(float)robotPositions[i][o][0]*100+"
            // "+(float)robotPositions[i][o][1]*100);
            teams[i].robot[o].body.setType(BodyType.DYNAMIC);
            teams[i].robot[o].body.setLinearVelocity(
                new Vec2(
                    (float) robotVelocitys[i][o][0] * 100, (float) robotVelocitys[i][o][1] * 100));
            teams[i].robot[o].body.setAngularVelocity((float) robotVelocitys[i][o][2]);
          }
        teams[1].ball.setType(BodyType.STATIC);
        teams[1].ball.setTransform(
            new Vec2((float) ballPosition[0] * 100, (float) ballPosition[1] * 100), 0);
        teams[1].ball.setType(BodyType.DYNAMIC);
        teams[1].ball.setLinearVelocity(
            new Vec2((float) ballVelocity[0] * 100, (float) ballVelocity[1] * 100));
        teams[1].ball.setAngularVelocity(0); // Not sent, reset it!

        // Simulate a frame!
        for (int i = 0; i < 2; i++) teams[i].gameFrame();

        // Send information to serial port!

        if (Server.useSerial) writeSerial(false);
      }
    } catch (IOException e) {
      Server.threads.remove(this);
      e.printStackTrace();
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  public synchronized void serialEvent(SerialPortEvent oEvent) {
    if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
      try {
        int available = input.available();
        if (count < BUF_SIZE) {
          count =
              count
                  + input.read(
                      buf, count, (available > BUF_SIZE - count) ? BUF_SIZE - count : available);
        }

        start = newline;
        for (int i = newline; i <= count; i++) {
          if (buf[i] == 'U' || buf[i] == '$') { // start byte
            start = i;
            break;
          }
        }
        for (int i = start; i <= count; i++) {
          if (buf[i] == '\n') { // end byte
            newline = i;
            break;
          }
        }

        if (newline <= start) {
          if (count == BUF_SIZE) { // buf full an no valid datagram found, clear the buffer
            // need to copy the stuff after the start byte to the beginning
            if ((count - start) <= 128) {
              System.arraycopy(buf, start, buf, 0, count - start);
              count = count - start;
              start = 0;
              newline = 0;
            } else { // buf full and last start byte is more than max packet size clear all of the
                     // buffer
              count = 0; // no need to copy since the buf is grabage
              start = 0;
              newline = 0;
            }
          }
          return;
        }

        if (buf[start] == '$') {
          buf[newline] = '\0';
          System.out.println(new String(buf, start + 1, newline - start));
        }

        // found valid datagram
        // parse datagram comma delimited
        buf[newline] = ',';
        String newbuf = new String(buf, start, newline - start);
        StringTokenizer st = new StringTokenizer(newbuf, ",");
        int[] data = new int[5];

        int i = 0;
        while (st.hasMoreElements() && i < 5) {
          String temp = st.nextToken();
          try {
            data[i] = Integer.parseInt(temp, 16);
          } catch (NumberFormatException e) {
          }
          i++;
        }

        RobotEvent ev = new RobotEvent();
        int checksum;

        ev.setCommand(EventEnum.getEvent(data[1]));
        ev.setIndex((short) data[2]);
        ev.setValue((int) data[3]);
        checksum = data[4];

        int checksum2 =
            (int)
                (((int) (ev.getCommand().getValue() & 0x000000FF)
                        + (int) (ev.getIndex() & 0x000000FF)
                        + (int) (ev.getValue() & 0x000000FF)
                        + (int) ((ev.getValue() & 0x0000FF00) >> 8))
                    % 256);

        if (checksum2 == checksum) {
          // log event received
          recv.put(ev);
        }
      } catch (Exception e) {
      }
    }
  }
Пример #10
0
  protected boolean executeUploadCommand(Collection commandDownloader) throws RunnerException {
    firstErrorFound = false; // haven't found any errors yet
    secondErrorFound = false;
    notFoundError = false;
    int result = 0; // pre-initialized to quiet a bogus warning from jikes

    String userdir = System.getProperty("user.dir") + File.separator;

    try {
      String[] commandArray = new String[commandDownloader.size()];
      commandDownloader.toArray(commandArray);

      String avrBasePath;

      if (Base.isLinux()) {
        avrBasePath = new String(Base.getHardwarePath() + "/tools/");
      } else {
        avrBasePath = new String(Base.getHardwarePath() + "/tools/avr/bin/");
      }

      commandArray[0] = avrBasePath + commandArray[0];

      if (verbose || Preferences.getBoolean("upload.verbose")) {
        for (int i = 0; i < commandArray.length; i++) {
          System.out.print(commandArray[i] + " ");
        }
        System.out.println();
      }
      Process process = Runtime.getRuntime().exec(commandArray);
      new MessageSiphon(process.getInputStream(), this);
      new MessageSiphon(process.getErrorStream(), this);

      // wait for the process to finish.  if interrupted
      // before waitFor returns, continue waiting
      //
      boolean compiling = true;
      while (compiling) {
        try {
          result = process.waitFor();
          compiling = false;
        } catch (InterruptedException intExc) {
        }
      }
      if (exception != null) {
        exception.hideStackTrace();
        throw exception;
      }
      if (result != 0) return false;
    } catch (Exception e) {
      String msg = e.getMessage();
      if ((msg != null)
          && (msg.indexOf("uisp: not found") != -1)
          && (msg.indexOf("avrdude: not found") != -1)) {
        // System.err.println("uisp is missing");
        // JOptionPane.showMessageDialog(editor.base,
        //                              "Could not find the compiler.\n" +
        //                              "uisp is missing from your PATH,\n" +
        //                              "see readme.txt for help.",
        //                              "Compiler error",
        //                              JOptionPane.ERROR_MESSAGE);
        return false;
      } else {
        e.printStackTrace();
        result = -1;
      }
    }
    // System.out.println("result2 is "+result);
    // if the result isn't a known, expected value it means that something
    // is fairly wrong, one possibility is that jikes has crashed.
    //
    if (exception != null) throw exception;

    if ((result != 0) && (result != 1)) {
      exception = new RunnerException(SUPER_BADNESS);
      // editor.error(exception);
      // PdeBase.openURL(BUGS_URL);
      // throw new PdeException(SUPER_BADNESS);
    }

    return (result == 0); // ? true : false;
  }
Пример #11
0
  // The user has done something.  respond to it.
  public void actionPerformed(ActionEvent e) {
    Object subject = e.getSource();

    if (subject == buttonZoomIn) {
      previewPane.ZoomIn();
      return;
    }
    if (subject == buttonZoomOut) {
      previewPane.ZoomOut();
      return;
    }
    if (subject == buttonOpenFile) {
      OpenFileDialog();
      return;
    }

    if (subject == buttonStart) {
      if (fileOpened) {
        paused = false;
        running = true;
        UpdateMenuBar();
        linesProcessed = 0;
        previewPane.setRunning(running);
        previewPane.setLinesProcessed(linesProcessed);
        statusBar.Start();
        SendFileCommand();
      }
      return;
    }
    if (subject == buttonPause) {
      if (running) {
        if (paused == true) {
          buttonPause.setText("Pause");
          paused = false;
          // @TODO: if the robot is not ready to unpause, this might fail and the program would
          // appear to hang.
          SendFileCommand();
        } else {
          buttonPause.setText("Unpause");
          paused = true;
        }
      }
      return;
    }
    if (subject == buttonDrive) {
      Drive();
      return;
    }
    if (subject == buttonHalt) {
      Halt();
      return;
    }
    if (subject == buttonRescan) {
      ListSerialPorts();
      UpdateMenuBar();
      return;
    }
    if (subject == buttonConfig) {
      UpdateConfig();
      return;
    }
    if (subject == buttonJogMotors) {
      JogMotors();
      return;
    }
    if (subject == buttonAbout) {
      JOptionPane.showMessageDialog(
          null,
          "Created by Dan Royer ([email protected]).\n\n"
              + "Find out more at http://www.marginallyclever.com/\n"
              + "Get the latest version and read the documentation online @ http://github.com/i-make-robots/DrawBot/");
      return;
    }
    if (subject == buttonCheckForUpdate) {
      CheckForUpdate();
      return;
    }
    if (subject == buttonExit) {
      System.exit(0); // @TODO: be more graceful?
      return;
    }

    int i;
    for (i = 0; i < 10; ++i) {
      if (subject == buttonRecent[i]) {
        OpenFileOnDemand(recentFiles[i]);
        return;
      }
    }

    for (i = 0; i < portsDetected.length; ++i) {
      if (subject == buttonPorts[i]) {
        OpenPort(portsDetected[i]);
        return;
      }
    }
  }
Пример #12
0
public class DrawbotGUI extends JPanel implements ActionListener, SerialPortEventListener {
  static final long serialVersionUID = 1;
  private static final String cue = "> ";
  private static final String eol = ";";
  private static final String NL = System.getProperty("line.separator");

  private static DrawbotGUI singletonObject;

  // Serial connection
  private static int BAUD_RATE = 57600;
  private CommPortIdentifier portIdentifier;
  private CommPort commPort;
  private SerialPort serialPort;
  private InputStream in;
  private OutputStream out;
  private String[] portsDetected;
  private boolean portOpened = false;
  private boolean portConfirmed = false;

  // Preferences
  private Preferences prefs = Preferences.userRoot().node("DrawBot");
  private String[] recentFiles;
  private String recentPort;

  // Robot config
  private long robot_uid = 0;

  private double limit_top = 10;
  private double limit_bottom = -10;
  private double limit_left = -10;
  private double limit_right = 10;

  // paper area (stock material
  private double paper_top = 10;
  private double paper_bottom = -10;
  private double paper_left = -10;
  private double paper_right = 10;
  private boolean m1invert = false;
  private boolean m2invert = false;

  // GUI elements
  private static JFrame mainframe;
  private JMenuBar menuBar;
  private JMenuItem buttonOpenFile, buttonExit;
  private JMenuItem buttonConfig, buttonRescan, buttonJogMotors;
  private JMenuItem buttonStart, buttonPause, buttonHalt, buttonDrive;
  private JMenuItem buttonZoomIn, buttonZoomOut;
  private JMenuItem buttonAbout, buttonCheckForUpdate;

  private JMenuItem[] buttonRecent = new JMenuItem[10];
  private JMenuItem[] buttonPorts;

  private JTextPane log;
  private JScrollPane logPane;
  HTMLEditorKit kit;
  HTMLDocument doc;

  private DrawPanel previewPane;
  private StatusBar statusBar;

  // parsing input from Drawbot
  private String line3 = "";

  // reading file
  private boolean running = false;
  private boolean paused = true;
  private long linesTotal = 0;
  private long linesProcessed = 0;
  private boolean fileOpened = false;
  private ArrayList<String> gcode;
  private float estimated_time = 0;

  // Singleton stuff
  private DrawbotGUI() {
    LoadConfig();
    GetRecentFiles();
    GetRecentPaperSize();
    GetRecentPort();
  }

  public static DrawbotGUI getSingleton() {
    if (singletonObject == null) {
      singletonObject = new DrawbotGUI();
    }
    return singletonObject;
  }

  //  data access
  public ArrayList<String> getGcode() {
    return gcode;
  }

  // manages the vertical split in the GUI
  public class Splitter extends JSplitPane {
    static final long serialVersionUID = 1;

    public Splitter(int split_direction) {
      super(split_direction);
      setResizeWeight(0.9);
      setDividerLocation(0.9);
    }
  }

  public void LoadImage(String filename) {
    BufferedImage img;

    try {
      img = ImageIO.read(new File(filename));

      Filter_BlackAndWhite bwc = new Filter_BlackAndWhite();
      img = bwc.Process(img);

      Filter_Resize rs =
          new Filter_Resize(paper_top, paper_bottom, paper_left, paper_right, 1, 0.9f);
      img = rs.Process(img);

      Filter_DitherFloydSteinberg dither = new Filter_DitherFloydSteinberg();
      img = dither.Process(img);

      String ngcPair = filename.substring(0, filename.lastIndexOf('.')) + ".ngc";
      Filter_TSPGcodeGenerator tsp = new Filter_TSPGcodeGenerator(ngcPair);
      tsp.Process(img);
    } catch (IOException e) {
    }
  }

  // returns angle of dy/dx as a value from 0...2PI
  private double atan3(double dy, double dx) {
    double a = Math.atan2(dy, dx);
    if (a < 0) a = (Math.PI * 2.0) + a;
    return a;
  }

  // appends a message to the log tab and system out.
  public void Log(String msg) {
    try {
      kit.insertHTML(doc, doc.getLength(), msg, 0, 0, null);
      int over_length = doc.getLength() - msg.length() - 5000;
      doc.remove(0, over_length);
    } catch (BadLocationException e) {
      // TODO Auto-generated catch block
      // e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      // e.printStackTrace();
    }
  }

  public void ClearLog() {
    try {
      doc.replace(0, doc.getLength(), "", null);
      kit.insertHTML(doc, 0, "", 0, 0, null);
    } catch (BadLocationException e) {
      // TODO Auto-generated catch block
      // e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      // e.printStackTrace();
    }
  }

  public void ClosePort() {
    if (portOpened) {
      if (serialPort != null) {
        try {
          // Close the I/O streams.
          out.close();
          in.close();
          // Close the port.
          serialPort.removeEventListener();
          serialPort.close();
        } catch (IOException e) {
          // Don't care
        }
      }

      ClearLog();
      portOpened = false;
      portConfirmed = false;
      previewPane.setConnected(false);
      UpdateMenuBar();
    }
  }

  // open a serial connection to a device.  We won't know it's the robot until
  public int OpenPort(String portName) {
    if (portOpened && portName.equals(recentPort)) return 0;

    ClosePort();

    Log("<font color='green'>Connecting to " + portName + "...</font>\n");

    // find the port
    try {
      portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
    } catch (Exception e) {
      Log("<span style='color:red'>Ports could not be identified:" + e.getMessage() + "</span>\n");
      e.printStackTrace();
      return 1;
    }

    if (portIdentifier.isCurrentlyOwned()) {
      Log(
          "<span style='color:red'>Error: Another program is currently using this port."
              + "</span>\n");
      return 2;
    }

    // open the port
    try {
      commPort = portIdentifier.open("DrawbotGUI", 2000);
    } catch (Exception e) {
      Log("Port could not be opened:" + e.getMessage() + NL);
      e.printStackTrace();
      return 3;
    }

    if ((commPort instanceof SerialPort) == false) {
      Log("<span style='color:red'>Only serial ports are handled by this example." + "</span>\n");
      return 4;
    }

    // set the port parameters (like baud rate)
    serialPort = (SerialPort) commPort;
    try {
      serialPort.setSerialPortParams(
          BAUD_RATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
    } catch (Exception e) {
      Log("<span style='color:red'>Port could not be configured:" + e.getMessage() + "</span>\n");
      return 5;
    }

    try {
      in = serialPort.getInputStream();
      out = serialPort.getOutputStream();
    } catch (Exception e) {
      Log("<span style='color:red'>Streams could not be opened:" + e.getMessage() + "</span>\n");
      return 6;
    }

    try {
      serialPort.addEventListener(this);
      serialPort.notifyOnDataAvailable(true);
    } catch (TooManyListenersException e) {
      Log("<span style='color:red'>Streams could not be opened:" + e.getMessage() + "</span>\n");
      return 7;
    }

    Log("<span style='green'>Opened.</span>\n");
    SetRecentPort(portName);
    portOpened = true;
    UpdateMenuBar();

    return 0;
  }

  /**
   * Complete the handshake, load robot-specific configuration, update the menu, repaint the preview
   * with the limits.
   *
   * @return true if handshake succeeds.
   */
  public boolean ConfirmPort() {
    if (portConfirmed == true) return true;
    String hello = "HELLO WORLD! I AM DRAWBOT #";
    int found = line3.lastIndexOf(hello);
    if (found >= 0) {
      portConfirmed = true;

      // get the UID reported by the robot
      String[] lines = line3.substring(found + hello.length()).split("\\r?\\n");
      if (lines.length > 0) {
        try {
          robot_uid = Long.parseLong(lines[0]);
        } catch (NumberFormatException e) {
        }
      }

      // new robots have UID=0
      if (robot_uid == 0) GetNewRobotUID();

      mainframe.setTitle("Drawbot #" + Long.toString(robot_uid) + " connected");

      // load machine specific config
      GetRecentPaperSize();
      LoadConfig();
      if (limit_top == 0 && limit_bottom == 0 && limit_left == 0 && limit_right == 0) {
        UpdateConfig();
      }

      previewPane.setMachineLimits(limit_top, limit_bottom, limit_left, limit_right);
      SendConfig();

      UpdateMenuBar();
      previewPane.setConnected(true);
    }
    return portConfirmed;
  }

  /** based on http://www.exampledepot.com/egs/java.net/Post.html */
  private void GetNewRobotUID() {
    try {
      // Send data
      URL url = new URL("http://marginallyclever.com/drawbot_getuid.php");
      URLConnection conn = url.openConnection();
      BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
      robot_uid = Long.parseLong(rd.readLine());
      rd.close();
    } catch (Exception e) {
    }

    // did read go ok?
    if (robot_uid != 0) {
      SendLineToRobot("UID " + robot_uid);
    }
  }

  // find all available serial ports for the settings->ports menu.
  public String[] ListSerialPorts() {
    @SuppressWarnings("unchecked")
    Enumeration<CommPortIdentifier> ports =
        (Enumeration<CommPortIdentifier>) CommPortIdentifier.getPortIdentifiers();
    ArrayList<String> portList = new ArrayList<String>();
    while (ports.hasMoreElements()) {
      CommPortIdentifier port = (CommPortIdentifier) ports.nextElement();
      if (port.getPortType() == CommPortIdentifier.PORT_SERIAL) {
        portList.add(port.getName());
      }
    }
    portsDetected = (String[]) portList.toArray(new String[0]);
    return portsDetected;
  }

  // pull the last connected port from prefs
  public void GetRecentPort() {
    recentPort = prefs.get("recent-port", "");
  }

  // update the prefs with the last port connected and refreshes the menus.
  // @TODO: only update when the port is confirmed?
  public void SetRecentPort(String portName) {
    prefs.put("recent-port", portName);
    recentPort = portName;
    UpdateMenuBar();
  }

  // save paper limits
  public void SetRecentPaperSize() {
    String id = Long.toString(robot_uid);
    prefs.putDouble(id + "_paper_left", paper_left);
    prefs.putDouble(id + "_paper_right", paper_right);
    prefs.putDouble(id + "_paper_top", paper_top);
    prefs.putDouble(id + "_paper_bottom", paper_bottom);
    previewPane.setPaperSize(paper_top, paper_bottom, paper_left, paper_right);
  }

  public void GetRecentPaperSize() {
    String id = Long.toString(robot_uid);
    paper_left = Double.parseDouble(prefs.get(id + "_paper_left", "0"));
    paper_right = Double.parseDouble(prefs.get(id + "_paper_right", "0"));
    paper_top = Double.parseDouble(prefs.get(id + "_paper_top", "0"));
    paper_bottom = Double.parseDouble(prefs.get(id + "_paper_bottom", "0"));
  }

  // close the file, clear the preview tab
  public void CloseFile() {
    if (fileOpened == true) {
      fileOpened = false;
    }
  }

  void EstimateDrawTime() {
    int i, j;

    float drawScale = 1.0f;
    double px = 0, py = 0, pz = 0;
    float feed_rate = 1.0f;

    estimated_time = 0;
    float estimated_length = 0;
    int estimate_count = 0;

    for (i = 0; i < gcode.size(); ++i) {
      String line = gcode.get(i);
      String[] pieces = line.split(";");
      if (pieces.length == 0) continue;

      String[] tokens = pieces[0].split("\\s");
      if (tokens.length == 0) continue;

      for (j = 0; j < tokens.length; ++j) {
        if (tokens[j].equals("G20")) drawScale = 0.393700787f;
        if (tokens[j].equals("G21")) drawScale = 0.1f;
        if (tokens[j].startsWith("F")) {
          feed_rate = Float.valueOf(tokens[j].substring(1)) * drawScale;
          Log("<span style='color:green'>feed rate=" + feed_rate + "</span>\n");
          feed_rate *= 1;
        }
      }

      double x = px;
      double y = py;
      double z = pz;
      double ai = px;
      double aj = py;
      for (j = 1; j < tokens.length; ++j) {
        if (tokens[j].startsWith("X")) x = Float.valueOf(tokens[j].substring(1)) * drawScale;
        if (tokens[j].startsWith("Y")) y = Float.valueOf(tokens[j].substring(1)) * drawScale;
        if (tokens[j].startsWith("Z")) z = Float.valueOf(tokens[j].substring(1)) * drawScale;
        if (tokens[j].startsWith("I")) ai = px + Float.valueOf(tokens[j].substring(1)) * drawScale;
        if (tokens[j].startsWith("J")) aj = py + Float.valueOf(tokens[j].substring(1)) * drawScale;
      }

      if (tokens[0].equals("G00")
          || tokens[0].equals("G0")
          || tokens[0].equals("G01")
          || tokens[0].equals("G1")) {
        // draw a line
        double ddx = x - px;
        double ddy = y - py;
        double dd = Math.sqrt(ddx * ddx + ddy * ddy);
        estimated_time += dd / feed_rate;
        estimated_length += dd;
        ++estimate_count;
        px = x;
        py = y;
        pz = z;
      } else if (tokens[0].equals("G02")
          || tokens[0].equals("G2")
          || tokens[0].equals("G03")
          || tokens[0].equals("G3")) {
        // draw an arc
        int dir = (tokens[0].equals("G02") || tokens[0].equals("G2")) ? -1 : 1;
        double dx = px - ai;
        double dy = py - aj;
        double radius = Math.sqrt(dx * dx + dy * dy);

        // find angle of arc (sweep)
        double angle1 = atan3(dy, dx);
        double angle2 = atan3(y - aj, x - ai);
        double theta = angle2 - angle1;

        if (dir > 0 && theta < 0) angle2 += 2.0 * Math.PI;
        else if (dir < 0 && theta > 0) angle1 += 2.0 * Math.PI;

        theta = Math.abs(angle2 - angle1);

        // Draw the arc from a lot of little line segments.
        for (int k = 0; k <= theta * DrawPanel.STEPS_PER_DEGREE; ++k) {
          double angle3 =
              (angle2 - angle1) * ((double) k / (theta * DrawPanel.STEPS_PER_DEGREE)) + angle1;
          float nx = (float) (ai + Math.cos(angle3) * radius);
          float ny = (float) (aj + Math.sin(angle3) * radius);

          double ddx = nx - px;
          double ddy = ny - py;
          double dd = Math.sqrt(ddx * ddx + ddy * ddy);
          estimated_time += dd / feed_rate;
          estimated_length += dd;
          ++estimate_count;
          px = nx;
          py = ny;
        }
        double ddx = x - px;
        double ddy = y - py;
        double dd = Math.sqrt(ddx * ddx + ddy * ddy);
        estimated_time += dd / feed_rate;
        estimated_length += dd;
        ++estimate_count;
        px = x;
        py = y;
        pz = z;
      }
    } // for ( each instruction )
    estimated_time += estimate_count * 0.007617845117845f;

    Log(
        "<font color='green'>"
            + estimate_count
            + " line segments.\n"
            + estimated_length
            + "cm of line.\n"
            + "Estimated "
            + statusBar.formatTime((long) (estimated_time * 10000))
            + "s to draw.</font>\n");
  }

  /**
   * Opens a file. If the file can be opened, get a drawing time estimate, update recent files list,
   * and repaint the preview tab.
   *
   * @param filename what file to open
   */
  public void LoadGCode(String filename) {
    CloseFile();

    try {
      Scanner scanner = new Scanner(new FileInputStream(filename));
      linesTotal = 0;
      gcode = new ArrayList<String>();
      try {
        while (scanner.hasNextLine()) {
          gcode.add(scanner.nextLine());
          ++linesTotal;
        }
      } finally {
        scanner.close();
      }
    } catch (IOException e) {
      Log("<span style='color:red'>File could not be opened.</span>\n");
      RemoveRecentFile(filename);
      return;
    }

    previewPane.setGCode(gcode);
    fileOpened = true;

    EstimateDrawTime();
    UpdateRecentFiles(filename);
    Halt();
  }

  /**
   * changes the order of the recent files list in the File submenu, saves the updated prefs, and
   * refreshes the menus.
   *
   * @param filename the file to push to the top of the list.
   */
  public void UpdateRecentFiles(String filename) {
    int cnt = recentFiles.length;
    String[] newFiles = new String[cnt];

    newFiles[0] = filename;

    int i, j = 1;
    for (i = 0; i < cnt; ++i) {
      if (!filename.equals(recentFiles[i]) && recentFiles[i] != "") {
        newFiles[j++] = recentFiles[i];
        if (j == cnt) break;
      }
    }

    recentFiles = newFiles;

    // update prefs
    for (i = 0; i < cnt; ++i) {
      if (recentFiles[i] != null && !recentFiles[i].isEmpty()) {
        prefs.put("recent-files-" + i, recentFiles[i]);
      }
    }

    UpdateMenuBar();
  }

  // A file failed to load.  Remove it from recent files, refresh the menu bar.
  public void RemoveRecentFile(String filename) {
    int i;
    for (i = 0; i < recentFiles.length - 1; ++i) {
      if (recentFiles[i] == filename) {
        break;
      }
    }
    for (; i < recentFiles.length - 1; ++i) {
      recentFiles[i] = recentFiles[i + 1];
    }
    recentFiles[recentFiles.length - 1] = "";

    // update prefs
    for (i = 0; i < recentFiles.length; ++i) {
      if (recentFiles[i] != null && !recentFiles[i].isEmpty()) {
        prefs.put("recent-files-" + i, recentFiles[i]);
      }
    }

    UpdateMenuBar();
  }

  // Load recent files from prefs
  public void GetRecentFiles() {
    recentFiles = new String[10];

    int i;
    for (i = 0; i < recentFiles.length; ++i) {
      recentFiles[i] = prefs.get("recent-files-" + i, "");
    }
  }

  // User has asked that a file be opened.
  public void OpenFileOnDemand(String filename) {
    Log("<font color='green'>Opening file " + recentFiles[0] + "...</font>\n");

    String ext = filename.substring(filename.lastIndexOf('.'));
    if (!ext.equalsIgnoreCase(".ngc")) {
      LoadImage(filename);
    } else {
      LoadGCode(filename);
    }

    statusBar.Clear();
  }

  // creates a file open dialog. If you don't cancel it opens that file.
  public void OpenFileDialog() {
    // Note: source for ExampleFileFilter can be found in FileChooserDemo,
    // under the demo/jfc directory in the Java 2 SDK, Standard Edition.
    String filename = (recentFiles[0].length() > 0) ? filename = recentFiles[0] : "";

    FileFilter filterImage =
        new FileNameExtensionFilter(
            "Images (jpg/bmp/png/gif)", "jpg", "jpeg", "png", "wbmp", "bmp", "gif");
    FileFilter filterGCODE = new FileNameExtensionFilter("GCODE files (ngc)", "ngc");

    JFileChooser fc = new JFileChooser(new File(filename));
    fc.addChoosableFileFilter(filterImage);
    fc.addChoosableFileFilter(filterGCODE);
    if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
      OpenFileOnDemand(fc.getSelectedFile().getAbsolutePath());
    }
  }

  public void GoHome() {
    SendLineToRobot("G00 X0 Y0 Z0");
  }

  /**
   * Open the config dialog, send the config update to the robot, save it for future, and refresh
   * the preview tab.
   */
  public void UpdateConfig() {
    final JDialog driver = new JDialog(mainframe, "Configure Limits", true);
    driver.setLayout(new GridBagLayout());

    final JTextField mtop = new JTextField(String.valueOf(limit_top));
    final JTextField mbottom = new JTextField(String.valueOf(limit_bottom));
    final JTextField mleft = new JTextField(String.valueOf(limit_left));
    final JTextField mright = new JTextField(String.valueOf(limit_right));

    final JTextField ptop = new JTextField(String.valueOf(paper_top));
    final JTextField pbottom = new JTextField(String.valueOf(paper_bottom));
    final JTextField pleft = new JTextField(String.valueOf(paper_left));
    final JTextField pright = new JTextField(String.valueOf(paper_right));

    final JButton cancel = new JButton("Cancel");
    final JButton save = new JButton("Save");

    GridBagConstraints c = new GridBagConstraints();
    c.gridx = 3;
    c.gridy = 0;
    driver.add(mtop, c);
    c.gridx = 3;
    c.gridy = 5;
    driver.add(mbottom, c);
    c.gridx = 0;
    c.gridy = 3;
    driver.add(mleft, c);
    c.gridx = 5;
    c.gridy = 3;
    driver.add(mright, c);

    c.gridx = 3;
    c.gridy = 1;
    driver.add(ptop, c);
    c.gridx = 3;
    c.gridy = 4;
    driver.add(pbottom, c);
    c.gridx = 1;
    c.gridy = 3;
    driver.add(pleft, c);
    c.gridx = 4;
    c.gridy = 3;
    driver.add(pright, c);

    c.gridx = 4;
    c.gridy = 6;
    driver.add(save, c);
    c.gridx = 5;
    c.gridy = 6;
    driver.add(cancel, c);

    Dimension s = ptop.getPreferredSize();
    s.width = 80;
    ptop.setPreferredSize(s);
    pbottom.setPreferredSize(s);
    pleft.setPreferredSize(s);
    pright.setPreferredSize(s);
    mtop.setPreferredSize(s);
    mbottom.setPreferredSize(s);
    mleft.setPreferredSize(s);
    mright.setPreferredSize(s);

    ActionListener driveButtons =
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            Object subject = e.getSource();
            if (subject == save) {
              paper_top = Float.valueOf(ptop.getText());
              paper_bottom = Float.valueOf(pbottom.getText());
              paper_right = Float.valueOf(pright.getText());
              paper_left = Float.valueOf(pleft.getText());
              limit_top = Float.valueOf(mtop.getText());
              limit_bottom = Float.valueOf(mbottom.getText());
              limit_right = Float.valueOf(mright.getText());
              limit_left = Float.valueOf(mleft.getText());
              previewPane.setMachineLimits(limit_top, limit_bottom, limit_left, limit_right);
              previewPane.setPaperSize(paper_top, paper_bottom, paper_left, paper_right);
              SetRecentPaperSize();
              SaveConfig();
              SendConfig();
              driver.dispose();
            }
            if (subject == cancel) {
              driver.dispose();
            }
          }
        };

    save.addActionListener(driveButtons);
    cancel.addActionListener(driveButtons);
    SendLineToRobot("M114"); // "where" command
    driver.pack();
    driver.setVisible(true);
  }

  void LoadConfig() {
    String id = Long.toString(robot_uid);
    limit_top = Double.valueOf(prefs.get(id + "_limit_top", "0"));
    limit_bottom = Double.valueOf(prefs.get(id + "_limit_bottom", "0"));
    limit_left = Double.valueOf(prefs.get(id + "_limit_left", "0"));
    limit_right = Double.valueOf(prefs.get(id + "_limit_right", "0"));
    m1invert = Boolean.parseBoolean(prefs.get(id + "_m1invert", "false"));
    m2invert = Boolean.parseBoolean(prefs.get(id + "_m2invert", "false"));
  }

  void SaveConfig() {
    String id = Long.toString(robot_uid);
    prefs.put(id + "_limit_top", Double.toString(limit_top));
    prefs.put(id + "_limit_bottom", Double.toString(limit_bottom));
    prefs.put(id + "_limit_right", Double.toString(limit_right));
    prefs.put(id + "_limit_left", Double.toString(limit_left));
    prefs.put(id + "_m1invert", Boolean.toString(m1invert));
    prefs.put(id + "_m2invert", Boolean.toString(m2invert));
  }

  void SendConfig() {
    if (!portConfirmed) return;

    // Send a command to the robot with new configuration values
    String line =
        "CONFIG T"
            + limit_top
            + " B"
            + limit_bottom
            + " L"
            + limit_left
            + " R"
            + limit_right
            + " I"
            + (m1invert ? "-1" : "1")
            + " J"
            + (m2invert ? "-1" : "1");
    SendLineToRobot(line);
    SendLineToRobot("TELEPORT X0 Y0 Z0");
  }

  // Take the next line from the file and send it to the robot, if permitted.
  public void SendFileCommand() {
    if (running == false
        || paused == true
        || fileOpened == false
        || portConfirmed == false
        || linesProcessed >= linesTotal) return;

    String line;
    do {
      // are there any more commands?
      line = gcode.get((int) linesProcessed++).trim();
      previewPane.setLinesProcessed(linesProcessed);
      statusBar.SetProgress(linesProcessed, linesTotal);
      // loop until we find a line that gets sent to the robot, at which point we'll
      // pause for the robot to respond.  Also stop at end of file.
    } while (ProcessLine(line) && linesProcessed < linesTotal);

    if (linesProcessed == linesTotal) {
      // end of file
      Halt();
    }
  }

  /**
   * removes comments, processes commands drawbot shouldn't have to handle.
   *
   * @param line command to send
   * @return true if the robot is ready for another command to be sent.
   */
  public boolean ProcessLine(String line) {
    // tool change request?
    String[] tokens = line.split("\\s");

    // tool change?
    if (Arrays.asList(tokens).contains("M06") || Arrays.asList(tokens).contains("M6")) {
      for (int i = 0; i < tokens.length; ++i) {
        if (tokens[i].startsWith("T")) {
          JOptionPane.showMessageDialog(
              null, "Please change to tool #" + tokens[i].substring(1) + " and click OK.");
        }
      }
      // still ready to send
      return true;
    }

    // end of program?
    if (tokens[0] == "M02" || tokens[0] == "M2") {
      Halt();
      return false;
    }

    // other machine code to ignore?
    if (tokens[0].startsWith("M")) {
      Log("<font color='pink'>" + line + "</font>\n");
      return true;
    }

    // contains a comment?  if so remove it
    int index = line.indexOf('(');
    if (index != -1) {
      String comment = line.substring(index + 1, line.lastIndexOf(')'));
      line = line.substring(0, index).trim();
      Log("<font color='grey'>* " + comment + "</font\n");
      if (line.length() == 0) {
        // entire line was a comment.
        return true; // still ready to send
      }
    }

    // send relevant part of line to the robot
    SendLineToRobot(line);

    return false;
  }

  /**
   * Sends a single command the robot. Could be anything.
   *
   * @param line command to send.
   * @return true means the command is sent. false means it was not.
   */
  public void SendLineToRobot(String line) {
    if (!portConfirmed) return;

    line += eol;
    Log("<font color='white'>" + line + "</font>");
    try {
      out.write(line.getBytes());
    } catch (IOException e) {
    }
  }

  /**
   * stop sending file commands to the robot.
   *
   * @todo add an e-stop command?
   */
  public void Halt() {
    running = false;
    paused = false;
    linesProcessed = 0;
    previewPane.setLinesProcessed(0);
    previewPane.setRunning(running);
    UpdateMenuBar();
  }

  // The user has done something.  respond to it.
  public void actionPerformed(ActionEvent e) {
    Object subject = e.getSource();

    if (subject == buttonZoomIn) {
      previewPane.ZoomIn();
      return;
    }
    if (subject == buttonZoomOut) {
      previewPane.ZoomOut();
      return;
    }
    if (subject == buttonOpenFile) {
      OpenFileDialog();
      return;
    }

    if (subject == buttonStart) {
      if (fileOpened) {
        paused = false;
        running = true;
        UpdateMenuBar();
        linesProcessed = 0;
        previewPane.setRunning(running);
        previewPane.setLinesProcessed(linesProcessed);
        statusBar.Start();
        SendFileCommand();
      }
      return;
    }
    if (subject == buttonPause) {
      if (running) {
        if (paused == true) {
          buttonPause.setText("Pause");
          paused = false;
          // @TODO: if the robot is not ready to unpause, this might fail and the program would
          // appear to hang.
          SendFileCommand();
        } else {
          buttonPause.setText("Unpause");
          paused = true;
        }
      }
      return;
    }
    if (subject == buttonDrive) {
      Drive();
      return;
    }
    if (subject == buttonHalt) {
      Halt();
      return;
    }
    if (subject == buttonRescan) {
      ListSerialPorts();
      UpdateMenuBar();
      return;
    }
    if (subject == buttonConfig) {
      UpdateConfig();
      return;
    }
    if (subject == buttonJogMotors) {
      JogMotors();
      return;
    }
    if (subject == buttonAbout) {
      JOptionPane.showMessageDialog(
          null,
          "Created by Dan Royer ([email protected]).\n\n"
              + "Find out more at http://www.marginallyclever.com/\n"
              + "Get the latest version and read the documentation online @ http://github.com/i-make-robots/DrawBot/");
      return;
    }
    if (subject == buttonCheckForUpdate) {
      CheckForUpdate();
      return;
    }
    if (subject == buttonExit) {
      System.exit(0); // @TODO: be more graceful?
      return;
    }

    int i;
    for (i = 0; i < 10; ++i) {
      if (subject == buttonRecent[i]) {
        OpenFileOnDemand(recentFiles[i]);
        return;
      }
    }

    for (i = 0; i < portsDetected.length; ++i) {
      if (subject == buttonPorts[i]) {
        OpenPort(portsDetected[i]);
        return;
      }
    }
  }

  // Deal with something robot has sent.
  public void serialEvent(SerialPortEvent events) {
    switch (events.getEventType()) {
      case SerialPortEvent.DATA_AVAILABLE:
        try {
          final byte[] buffer = new byte[1024];
          int len = in.read(buffer);
          if (len > 0) {
            String line2 = new String(buffer, 0, len);
            Log("<span style='color:#FFA500'>" + line2.replace("\n", "<br>") + "</span>");
            line3 += line2;
            // wait for the cue ("> ") to send another command
            if (line3.lastIndexOf(cue) != -1) {
              if (ConfirmPort()) {
                line3 = "";
                SendFileCommand();
              }
            }
          }
        } catch (IOException e) {
        }
        break;
    }
  }

  public void CheckForUpdate() {
    /*
    try {
        // Send data
    	URL url = new URL("http://marginallyclever.com/drawbot_get.php");
        URLConnection conn = url.openConnection();
        BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        robot_uid = Long.parseLong(rd.readLine());
        rd.close();
    } catch (Exception e) {}
     */
    // TODO Get latest version
    // TODO Offer to download latest version?
  }

  /** Open the config dialog, update the paper size, refresh the preview tab. */
  public void Drive() {
    JDialog driver = new JDialog(mainframe, "Manual Control", true);
    driver.setLayout(new GridBagLayout());

    JButton find = new JButton("FIND HOME");
    JButton home = new JButton("GO HOME");
    JButton center = new JButton("THIS IS HOME");

    JButton up1 = new JButton("Y1");
    JButton up10 = new JButton("Y10");
    JButton up100 = new JButton("Y100");

    JButton down1 = new JButton("Y-1");
    JButton down10 = new JButton("Y-10");
    JButton down100 = new JButton("Y-100");

    JButton left1 = new JButton("X-1");
    JButton left10 = new JButton("X-10");
    JButton left100 = new JButton("X-100");

    JButton right1 = new JButton("X1");
    JButton right10 = new JButton("X10");
    JButton right100 = new JButton("X100");

    GridBagConstraints c = new GridBagConstraints();
    c.gridx = 3;
    c.gridy = 0;
    driver.add(up100, c);
    c.gridx = 3;
    c.gridy = 1;
    driver.add(up10, c);
    c.gridx = 3;
    c.gridy = 2;
    driver.add(up1, c);
    c.gridx = 3;
    c.gridy = 4;
    driver.add(down1, c);
    c.gridx = 3;
    c.gridy = 5;
    driver.add(down10, c);
    c.gridx = 3;
    c.gridy = 6;
    driver.add(down100, c);

    c.gridx = 0;
    c.gridy = 3;
    driver.add(left100, c);
    c.gridx = 1;
    c.gridy = 3;
    driver.add(left10, c);
    c.gridx = 2;
    c.gridy = 3;
    driver.add(left1, c);
    c.gridx = 4;
    c.gridy = 3;
    driver.add(right1, c);
    c.gridx = 5;
    c.gridy = 3;
    driver.add(right10, c);
    c.gridx = 6;
    c.gridy = 3;
    driver.add(right100, c);

    c.gridx = 3;
    c.gridy = 3;
    driver.add(home, c);
    c.gridx = 6;
    c.gridy = 0;
    driver.add(center, c);
    c.gridx = 6;
    c.gridy = 1;
    driver.add(find, c);

    ActionListener driveButtons =
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            Object subject = e.getSource();
            JButton b = (JButton) subject;
            String t = b.getText();
            if (t == "GO HOME") {
              GoHome();
              SendLineToRobot("M114");
            } else if (t == "FIND HOME") {
              SendLineToRobot("G28");
            } else if (t == "THIS IS HOME") {
              SendLineToRobot("TELEPORT XO YO");
            } else {
              SendLineToRobot("G91");
              SendLineToRobot("G00 " + b.getText());
              SendLineToRobot("G90");
              SendLineToRobot("M114");
            }
          }
        };

    up1.addActionListener(driveButtons);
    up10.addActionListener(driveButtons);
    up100.addActionListener(driveButtons);
    down1.addActionListener(driveButtons);
    down10.addActionListener(driveButtons);
    down100.addActionListener(driveButtons);
    left1.addActionListener(driveButtons);
    left10.addActionListener(driveButtons);
    left100.addActionListener(driveButtons);
    right1.addActionListener(driveButtons);
    right10.addActionListener(driveButtons);
    right100.addActionListener(driveButtons);
    center.addActionListener(driveButtons);
    home.addActionListener(driveButtons);
    find.addActionListener(driveButtons);
    SendLineToRobot("M114");
    driver.pack();
    driver.setVisible(true);
  }

  protected void JogMotors() {
    JDialog driver = new JDialog(mainframe, "Jog Motors", true);
    driver.setLayout(new GridBagLayout());
    GridBagConstraints c = new GridBagConstraints();

    final JButton buttonAneg = new JButton("IN");
    final JButton buttonApos = new JButton("OUT");
    final JCheckBox m1i = new JCheckBox("Invert", m1invert);

    final JButton buttonBneg = new JButton("IN");
    final JButton buttonBpos = new JButton("OUT");
    final JCheckBox m2i = new JCheckBox("Invert", m2invert);

    c.gridx = 0;
    c.gridy = 0;
    driver.add(new JLabel("L"), c);
    c.gridx = 0;
    c.gridy = 1;
    driver.add(new JLabel("R"), c);

    c.gridx = 1;
    c.gridy = 0;
    driver.add(buttonAneg, c);
    c.gridx = 1;
    c.gridy = 1;
    driver.add(buttonBneg, c);

    c.gridx = 2;
    c.gridy = 0;
    driver.add(buttonApos, c);
    c.gridx = 2;
    c.gridy = 1;
    driver.add(buttonBpos, c);

    c.gridx = 3;
    c.gridy = 0;
    driver.add(m1i, c);
    c.gridx = 3;
    c.gridy = 1;
    driver.add(m2i, c);

    ActionListener driveButtons =
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            Object subject = e.getSource();
            if (subject == buttonApos) SendLineToRobot("D00 L100");
            if (subject == buttonAneg) SendLineToRobot("D00 L-100");
            if (subject == buttonBpos) SendLineToRobot("D00 R100");
            if (subject == buttonBneg) SendLineToRobot("D00 R-100");
            SendLineToRobot("M114");
          }
        };

    ActionListener invertButtons =
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            m1invert = m1i.isSelected();
            m2invert = m2i.isSelected();

            SaveConfig();
            SendConfig();
          }
        };

    buttonApos.addActionListener(driveButtons);
    buttonAneg.addActionListener(driveButtons);

    buttonBpos.addActionListener(driveButtons);
    buttonBneg.addActionListener(driveButtons);

    m1i.addActionListener(invertButtons);
    m2i.addActionListener(invertButtons);

    SendLineToRobot("M114");
    driver.pack();
    driver.setVisible(true);
  }

  public JMenuBar CreateMenuBar() {
    // If the menu bar exists, empty it.  If it doesn't exist, create it.
    menuBar = new JMenuBar();

    UpdateMenuBar();

    return menuBar;
  }

  // Rebuild the contents of the menu based on current program state
  public void UpdateMenuBar() {
    JMenu menu;
    int i;

    menuBar.removeAll();

    // Build the first menu.
    menu = new JMenu("File");
    menu.setMnemonic(KeyEvent.VK_F);
    menuBar.add(menu);

    buttonOpenFile = new JMenuItem("Open File...", KeyEvent.VK_O);
    buttonOpenFile.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.ALT_MASK));
    buttonOpenFile.getAccessibleContext().setAccessibleDescription("Open a g-code file...");
    buttonOpenFile.addActionListener(this);
    menu.add(buttonOpenFile);

    menu.addSeparator();

    // list recent files
    if (recentFiles != null && recentFiles.length > 0) {
      // list files here
      for (i = 0; i < recentFiles.length; ++i) {
        if (recentFiles[i] == null || recentFiles[i].length() == 0) break;
        buttonRecent[i] = new JMenuItem((1 + i) + " " + recentFiles[i], KeyEvent.VK_1 + i);
        if (buttonRecent[i] != null) {
          buttonRecent[i].addActionListener(this);
          menu.add(buttonRecent[i]);
        }
      }
      if (i != 0) menu.addSeparator();
    }

    buttonExit = new JMenuItem("Exit", KeyEvent.VK_Q);
    buttonExit.getAccessibleContext().setAccessibleDescription("Goodbye...");
    buttonExit.addActionListener(this);
    menu.add(buttonExit);

    menuBar.add(menu);

    // settings menu
    menu = new JMenu("Settings");
    menu.setMnemonic(KeyEvent.VK_T);
    menu.getAccessibleContext().setAccessibleDescription("Adjust the robot settings.");

    JMenu subMenu = new JMenu("Port");
    subMenu.setMnemonic(KeyEvent.VK_P);
    subMenu.getAccessibleContext().setAccessibleDescription("What port to connect to?");
    subMenu.setEnabled(!running);
    ButtonGroup group = new ButtonGroup();

    ListSerialPorts();
    buttonPorts = new JRadioButtonMenuItem[portsDetected.length];
    for (i = 0; i < portsDetected.length; ++i) {
      buttonPorts[i] = new JRadioButtonMenuItem(portsDetected[i]);
      if (recentPort.equals(portsDetected[i]) && portOpened) {
        buttonPorts[i].setSelected(true);
      }
      buttonPorts[i].addActionListener(this);
      group.add(buttonPorts[i]);
      subMenu.add(buttonPorts[i]);
    }

    subMenu.addSeparator();

    buttonRescan = new JMenuItem("Rescan", KeyEvent.VK_N);
    buttonRescan.getAccessibleContext().setAccessibleDescription("Rescan the available ports.");
    buttonRescan.addActionListener(this);
    subMenu.add(buttonRescan);

    menu.add(subMenu);

    buttonConfig = new JMenuItem("Configure limits", KeyEvent.VK_L);
    buttonConfig.getAccessibleContext().setAccessibleDescription("Adjust the robot & paper shape.");
    buttonConfig.addActionListener(this);
    buttonConfig.setEnabled(portConfirmed && !running);
    menu.add(buttonConfig);

    buttonJogMotors = new JMenuItem("Jog Motors", KeyEvent.VK_J);
    buttonJogMotors.addActionListener(this);
    buttonJogMotors.setEnabled(portConfirmed && !running);
    menu.add(buttonJogMotors);

    buttonDrive = new JMenuItem("Drive Manually", KeyEvent.VK_R);
    buttonDrive.getAccessibleContext().setAccessibleDescription("Etch-a-sketch style driving");
    buttonDrive.addActionListener(this);
    buttonDrive.setEnabled(portConfirmed && !running);
    menu.add(buttonDrive);

    menuBar.add(menu);

    // Draw menu
    menu = new JMenu("Draw");
    menu.setMnemonic(KeyEvent.VK_D);
    menu.getAccessibleContext().setAccessibleDescription("Start & Stop progress");

    buttonStart = new JMenuItem("Start", KeyEvent.VK_S);
    buttonStart.getAccessibleContext().setAccessibleDescription("Start sending g-code");
    buttonStart.addActionListener(this);
    buttonStart.setEnabled(portConfirmed && !running);
    menu.add(buttonStart);

    buttonPause = new JMenuItem("Pause", KeyEvent.VK_P);
    buttonPause.getAccessibleContext().setAccessibleDescription("Pause sending g-code");
    buttonPause.addActionListener(this);
    buttonPause.setEnabled(portConfirmed && running);
    menu.add(buttonPause);

    buttonHalt = new JMenuItem("Halt", KeyEvent.VK_H);
    buttonHalt.getAccessibleContext().setAccessibleDescription("Halt sending g-code");
    buttonHalt.addActionListener(this);
    buttonHalt.setEnabled(portConfirmed && running);
    menu.add(buttonHalt);

    menuBar.add(menu);

    // tools menu
    menu = new JMenu("Tools");
    buttonZoomOut = new JMenuItem("Zoom -");
    buttonZoomOut.addActionListener(this);
    buttonZoomOut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, ActionEvent.ALT_MASK));
    menu.add(buttonZoomOut);

    buttonZoomIn = new JMenuItem("Zoom +", KeyEvent.VK_EQUALS);
    buttonZoomIn.addActionListener(this);
    buttonZoomIn.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, ActionEvent.ALT_MASK));
    menu.add(buttonZoomIn);

    menuBar.add(menu);

    // Help menu
    menu = new JMenu("Help");
    menu.setMnemonic(KeyEvent.VK_H);
    menu.getAccessibleContext().setAccessibleDescription("Get help");

    buttonAbout = new JMenuItem("About", KeyEvent.VK_A);
    menu.getAccessibleContext().setAccessibleDescription("Find out about this program");
    buttonAbout.addActionListener(this);
    menu.add(buttonAbout);

    buttonCheckForUpdate = new JMenuItem("Check for updates", KeyEvent.VK_U);
    menu.getAccessibleContext().setAccessibleDescription("Is there a newer version available?");
    buttonCheckForUpdate.addActionListener(this);
    buttonCheckForUpdate.setEnabled(false);
    menu.add(buttonCheckForUpdate);

    menuBar.add(menu);

    // finish
    menuBar.updateUI();
  }

  public Container CreateContentPane() {
    // Create the content-pane-to-be.
    JPanel contentPane = new JPanel(new BorderLayout());
    contentPane.setOpaque(true);

    // the log panel
    log = new JTextPane();
    log.setEditable(false);
    log.setBackground(Color.BLACK);
    logPane = new JScrollPane(log);
    kit = new HTMLEditorKit();
    doc = new HTMLDocument();
    log.setEditorKit(kit);
    log.setDocument(doc);
    DefaultCaret c = (DefaultCaret) log.getCaret();
    c.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
    ClearLog();

    // the preview panel
    previewPane = new DrawPanel();
    previewPane.setPaperSize(paper_top, paper_bottom, paper_left, paper_right);

    // status bar
    statusBar = new StatusBar();
    Font f = statusBar.getFont();
    statusBar.setFont(f.deriveFont(Font.BOLD, 15));
    Dimension d = statusBar.getMinimumSize();
    d.setSize(d.getWidth(), d.getHeight() + 30);
    statusBar.setMinimumSize(d);

    // layout
    Splitter split = new Splitter(JSplitPane.VERTICAL_SPLIT);
    split.add(previewPane);
    split.add(logPane);
    split.setDividerSize(8);

    contentPane.add(statusBar, BorderLayout.SOUTH);
    contentPane.add(split, BorderLayout.CENTER);

    // open the file
    if (recentFiles[0].length() > 0) {
      OpenFileOnDemand(recentFiles[0]);
    }

    // connect to the last port
    ListSerialPorts();
    if (Arrays.asList(portsDetected).contains(recentPort)) {
      OpenPort(recentPort);
    }

    return contentPane;
  }

  // Create the GUI and show it.  For thread safety, this method should be invoked from the
  // event-dispatching thread.
  private static void CreateAndShowGUI() {
    // Create and set up the window.
    mainframe = new JFrame("No Drawbot Connected");
    mainframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // Create and set up the content pane.
    DrawbotGUI demo = DrawbotGUI.getSingleton();
    mainframe.setJMenuBar(demo.CreateMenuBar());
    mainframe.setContentPane(demo.CreateContentPane());

    // Display the window.
    mainframe.setSize(800, 700);
    mainframe.setVisible(true);
  }

  public static void main(String[] args) {
    // Schedule a job for the event-dispatching thread:
    // creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(
        new Runnable() {
          public void run() {
            CreateAndShowGUI();
          }
        });
  }
}