public synchronized void notifyReply(SprogReply l) { // receive a reply message and log it
    SprogMessage msg;
    replyString = l.toString();
    nextLine("rep: \"" + replyString + "\"\n", "");

    // *** Check for error reply
    switch (state) {
      case IDLE:
        if (log.isDebugEnabled()) {
          log.debug("reply in IDLE state: " + replyString);
        }
        break;
      case CURRENTQUERYSENT:
        if (log.isDebugEnabled()) {
          log.debug("reply in CURRENTQUERYSENT state: " + replyString);
        }
        int valueLength = 4;
        if (sv.sprogType.sprogType >= SprogType.SPROGIIv3) {
          valueLength = 6;
        }
        tmpString =
            replyString.substring(
                replyString.indexOf("=") + 1, replyString.indexOf("=") + valueLength);
        if (log.isDebugEnabled()) {
          log.debug("Current limit string: " + tmpString);
        }
        // Value returned is number of ADC steps 0f 4.88mV across 0.47 ohms
        // SPROG 3 is equivalent, using .047R sense with 10x amplifier
        // Convert to milliAmps using integer math
        try {
          currentLimit = (Integer.parseInt(tmpString) * 4880) / 470;
        } catch (NumberFormatException e) {
          JOptionPane.showMessageDialog(
              null,
              "Malformed Reply for current limit",
              "SPROG Console",
              JOptionPane.ERROR_MESSAGE);
          state = State.IDLE;
          return;
        }
        if (log.isDebugEnabled()) {
          log.debug("Current limit value: " + currentLimit);
        }
        currentTextField.setText(String.valueOf(currentLimit));
        currentTextField.setEnabled(true);
        // Next get the mode word
        // Only get this if we have zero active slots in slot manager
        // in command mode
        if (sm.getInUseCount() == 0) {
          state = State.MODEQUERYSENT;
          msg = new SprogMessage(1);
          msg.setOpCode('M');
          nextLine("cmd: \"" + msg + "\"\n", "");
          tc.sendSprogMessage(msg, this);
        } else {
          state = State.IDLE;
        }
        break;
      case MODEQUERYSENT:
        if (log.isDebugEnabled()) {
          log.debug("reply in MODEQUERYSENT state: " + replyString);
        }
        tmpString =
            replyString.substring(replyString.indexOf("=") + 2, replyString.indexOf("=") + 6);
        // Value returned is in hex
        try {
          modeWord = Integer.parseInt(tmpString, 16);
        } catch (NumberFormatException e) {
          JOptionPane.showMessageDialog(
              null, "Malformed Reply for mode word", "SPROG Console", JOptionPane.ERROR_MESSAGE);
          state = State.IDLE;
          return;
        }
        state = State.IDLE;
        // Set Speed step radio buttons, etc., according to mode word
        if ((modeWord & SprogConstants.STEP14_BIT) != 0) {
          speed14Button.setSelected(true);
        } else if ((modeWord & SprogConstants.STEP28_BIT) != 0) {
          speed28Button.setSelected(true);
        } else {
          speed128Button.setSelected(true);
        }
        if ((modeWord & SprogConstants.ZTC_BIT) != 0) {
          ztcCheckBox.setSelected(true);
        }
        if ((modeWord & SprogConstants.BLUE_BIT) != 0) {
          blueCheckBox.setSelected(true);
        }
        break;
      case CURRENTSENT:
        if (log.isDebugEnabled()) {
          log.debug("reply in CURRENTSENT state: " + replyString);
        }
        // Get new mode word - assume 128 steps
        modeWord = SprogConstants.STEP128_BIT;
        if (speed14Button.isSelected()) {
          modeWord = modeWord & ~SprogConstants.STEP_MASK | SprogConstants.STEP14_BIT;
        } else if (speed28Button.isSelected()) {
          modeWord = modeWord & ~SprogConstants.STEP_MASK | SprogConstants.STEP28_BIT;
        }

        // ZTC mode
        if (ztcCheckBox.isSelected() == true) {
          modeWord = modeWord | SprogConstants.ZTC_BIT;
        }

        // Blueline mode
        if (blueCheckBox.isSelected() == true) {
          modeWord = modeWord | SprogConstants.BLUE_BIT;
        }

        // firmware unlock
        if (unlockCheckBox.isSelected() == true) {
          modeWord = modeWord | SprogConstants.UNLOCK_BIT;
        }

        // Send new mode word
        state = State.MODESENT;
        msg = new SprogMessage("M " + modeWord);
        nextLine("cmd: \"" + msg.toString() + "\"\n", "");
        tc.sendSprogMessage(msg, this);
        break;
      case MODESENT:
        if (log.isDebugEnabled()) {
          log.debug("reply in MODESENT state: " + replyString);
        }
        // Write to EEPROM
        state = State.WRITESENT;
        msg = new SprogMessage("W");
        nextLine("cmd: \"" + msg.toString() + "\"\n", "");
        tc.sendSprogMessage(msg, this);
        break;
      case WRITESENT:
        if (log.isDebugEnabled()) {
          log.debug("reply in WRITESENT state: " + replyString);
        }
        // All done
        state = State.IDLE;
    }
  }
  public synchronized void notifyVersion(SprogVersion v) {
    sv = v;
    if (log.isDebugEnabled()) {
      log.debug("Found: " + sv.toString());
    }
    if (sv.sprogType.isSprog() == false) {
      // Didn't recognize a SPROG so check if it is in boot mode already
      JOptionPane.showMessageDialog(
          null, "SPROG prompt not found", "SPROG Console", JOptionPane.ERROR_MESSAGE);
    } else {
      if (sv.sprogType.sprogType > SprogType.SPROGIIv3) {
        currentTextField.setToolTipText("Enter new current limit in milliAmps (less than 2500)");
      }
      // We know what we're connected to
      setTitle(title() + " - Connected to " + sv.toString());

      // Enable blueline & firmware unlock check boxes
      if (isBlueLineSupportPossible()) {
        if (log.isDebugEnabled()) {
          log.debug("Enable blueline check box");
        }
        blueCheckBox.setEnabled(true);
        if (log.isDebugEnabled()) {
          log.debug(Boolean.toString(blueCheckBox.isEnabled()));
        }
      }
      if (isFirmwareUnlockPossible()) {
        if (log.isDebugEnabled()) {
          log.debug("Enable firmware check box");
        }
        unlockCheckBox.setEnabled(true);
        if (log.isDebugEnabled()) {
          log.debug(Boolean.toString(unlockCheckBox.isEnabled()));
        }
      }

      ztcCheckBox.setEnabled(isZTCModePossible());

      // Get Current Limit if available
      if (isCurrentLimitPossible() && sm.getInUseCount() == 0) {
        state = State.CURRENTQUERYSENT;
        msg = new SprogMessage(1);
        msg.setOpCode('I');
        nextLine("cmd: \"" + msg + "\"\n", "");
        tc.sendSprogMessage(msg, this);
      } else {
        // Set default and get the mode word
        currentLimit = (SprogConstants.DEFAULT_I * 4880) / 470;
        currentTextField.setText(String.valueOf(SprogConstants.DEFAULT_I));

        // Only send this if we have zero active slots in slot manager
        // in command mode
        if (sm.getInUseCount() == 0) {
          state = State.MODEQUERYSENT;
          msg = new SprogMessage(1);
          msg.setOpCode('M');
          nextLine("cmd: \"" + msg + "\"\n", "");
          tc.sendSprogMessage(msg, this);
        } else {
          state = State.IDLE;
        }
      }
    }
  }