package lejos.pc.tools;
import lejos.pc.comm.*;
import lejos.nxt.remote.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.TableColumn;
import javax.swing.border.*;
import java.awt.event.*;
import java.io.*;
import java.text.NumberFormat;
/**
*
* Graphical control center for leJOS NXJ.
*
* @author Lawrie Griffiths
*/
public class NXJControl implements ListSelectionListener, NXTProtocol, DataViewerUI, ConsoleViewerUI {
// Constants
public static final int MAX_FILES = 30;
private static final int LCP = 0;
private static final int RCONSOLE = 1;
private static final int DATALOG = 2;
private static final Dimension frameSize = new Dimension(800, 620);
private static final Dimension filesAreaSize = new Dimension(780, 300);
private static final Dimension filesPanelSize = new Dimension(500, 400);
private static final Dimension nxtButtonsPanelSize = new Dimension(220, 130);
private static final Dimension filesButtonsPanelSize = new Dimension(700,100);
private static final Dimension nxtTableSize = new Dimension(500, 100);
private static final Dimension labelSize = new Dimension(60, 20);
private static final Dimension sliderSize = new Dimension(150, 50);
private static final Dimension tachoSize = new Dimension(100, 20);
private static final Dimension infoPanelSize = new Dimension(300, 110);
private static final Dimension namePanelSize = new Dimension(300, 110);
private static final Dimension innerInfoPanelSize = new Dimension(280, 70);
private static final Dimension tonePanelSize = new Dimension(300, 110);
private static final Dimension i2cPanelSize = new Dimension(480, 170);
private static final int fileNameColumnWidth = 200;
private static final String title = "NXJ Control Center";
private static final String[] sensorTypes = { "No Sensor", "Touch Sensor",
"Temperature", "RCX Light", "RCX Rotation", "Light Active",
"Light Inactive", "Sound DB", "Sound DBA", "Custom", "I2C",
"I2C 9V" };
private static final int[] sensorTypeValues = { NO_SENSOR, SWITCH, TEMPERATURE,
REFLECTION, ANGLE, LIGHT_ACTIVE, LIGHT_INACTIVE, SOUND_DB,
SOUND_DBA, CUSTOM, LOWSPEED, LOWSPEED_9V };
private static final String[] sensors = { "S1", "S2", "S3", "S4" };
private static final String[] sensorModes = { "Raw", "Boolean", "Percentage" };
private static final int[] sensorModeValues = { RAWMODE, BOOLEANMODE, PCTFULLSCALEMODE };
private final String[] motorNames = { "A", "B", "C" };
// GUI components
private Cursor hourglassCursor = new Cursor(Cursor.WAIT_CURSOR);
private Cursor normalCursor = new Cursor(Cursor.DEFAULT_CURSOR);
private JFrame frame = new JFrame(title);
private JTable nxtTable = new JTable();
private JScrollPane nxtTablePane;
private JTextField nameText = new JTextField(8);
private JTable table;
private JScrollPane tablePane;
private JPanel filesPanel = new JPanel();
private JPanel consolePanel = new JPanel();
private JPanel monitorPanel = new JPanel();
private JPanel controlPanel = new JPanel();
private JPanel dataPanel = new JPanel();
private JPanel otherPanel = new JPanel();
private JTextArea theConsoleLog = new JTextArea(22, 68);
private JTextArea theDataLog = new JTextArea(20, 68);
private LabeledGauge batteryGauge = new LabeledGauge("Battery", 10000);
private JSlider[] sliders = new JSlider[3];
private JLabel[] tachos = new JLabel[3];
private JCheckBox[] selectors = new JCheckBox[3];
private JCheckBox[] reversors = new JCheckBox[3];
private JTextField[] limits = new JTextField[3];
private JButton[] resetButtons = new JButton[3];
private JButton connectButton = new JButton("Connect");
private JButton dataDownloadButton = new JButton("Download");
private TextField dataColumns = new TextField("8", 2);
private JButton searchButton = new JButton("Search");
private JButton monitorUpdateButton = new JButton("Update");
private JButton forwardButton = new JButton("Forward");
private JButton backwardButton = new JButton("Backward");
private JButton leftButton = new JButton("Turn Left");
private JButton rightButton = new JButton("Turn Right");
private JButton deleteButton = new JButton("Delete Files");
private JButton uploadButton = new JButton("Upload file");
private JButton downloadButton = new JButton("Download file");
private JButton runButton = new JButton("Run program");
private JButton nameButton = new JButton("Set Name");
private JButton formatButton = new JButton("Format");
private JRadioButton usbButton = new JRadioButton("USB");
private JRadioButton bluetoothButton = new JRadioButton("Bluetooth");
private JRadioButton bothButton = new JRadioButton("Both", true);
private JRadioButton lcpButton = new JRadioButton("LCP", true);
private JRadioButton rconsoleButton = new JRadioButton("RConsole");
private JRadioButton datalogButton = new JRadioButton("Data Log");
private JFormattedTextField freq = new JFormattedTextField(new Integer(500));
private JFormattedTextField duration = new JFormattedTextField(new Integer(1000));
private JComboBox sensorList = new JComboBox(sensors);
private JComboBox sensorList2 = new JComboBox(sensors);
private JComboBox sensorModeList = new JComboBox(sensorModes);
private JComboBox sensorTypeList = new JComboBox(sensorTypes);
private SensorPanel[] sensorPanels = { new SensorPanel("Sensor Port 1"),
new SensorPanel("Sensor Port 2"), new SensorPanel("Sensor Port 3"),
new SensorPanel("Sensor Port 4") };
private JFormattedTextField txData = new JFormattedTextField();
private JFormattedTextField rxDataLength = new JFormattedTextField(new Integer(1));
private JLabel rxData = new JLabel();
private Border etchedBorder = BorderFactory.createEtchedBorder();
private JButton soundButton = new JButton("Play Sound File");
private JTextField newName = new JTextField(16);
private JTabbedPane tabbedPane = new JTabbedPane();
// Other instance data
private NXTConnectionModel nm;
private ExtendedFileModel fm;
private NXJControl control;
private NXTCommand nxtCommand;
private NXTCommand[] nxtCommands;
private NXTComm[] nxtComms;
private NXTConnector conn = new NXTConnector();
private NXTInfo[] nxts;
private InputValues[] sensorValues = new InputValues[4];
private int mv;
private int appProtocol = LCP;
private int rowLength = 8; // default
private int recordCount;
private DataViewComms dvc;
private DataViewComms[] dvcs;
private ConsoleViewComms cvc;
private ConsoleViewComms[] cvcs;
// Formatter
private static final NumberFormat FORMAT_FLOAT = NumberFormat.getNumberInstance();
/**
* Command line entry point
*/
public static void main(String args[]) {
try {
NXJControl instance = new NXJControl();
instance.run();
} catch (Throwable t) {
System.err.println("Error: " + t.getMessage());
}
}
/**
* Run the program
*/
private void run() {
// Close connection and exit when frame windows closed
WindowListener listener = new WindowAdapter() {
public void windowClosing(WindowEvent w) {
closeAll();
System.exit(0);
}
};
frame.addWindowListener(listener);
conn.addLogListener(new ToolsLogger());
control = this;
// Search Button: search for NXTs
searchButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
search();
}
});
// Connect Button: connect to selected NXT
connectButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
connect();
}
});
// Data log Connect Button: connect to the Data Logger
dataDownloadButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
recordCount = 0;
try {
rowLength = Integer.parseInt(dataColumns.getText());
} catch (NumberFormatException ex) {
System.out.println(dataColumns.getText() + " is not a number, default reset to 8");
}
dvc.startDownload();
}
});
// Monitor Update Button: get values being monitored
monitorUpdateButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
getSensorValues();
updateSensors();
}
});
lcpButton.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
if (appProtocol == getAppProtocol()) return;
if (lcpButton.isSelected()) {
createLCPTabs();
appProtocol = LCP;
}
}
});
rconsoleButton.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
if (appProtocol == getAppProtocol()) return;
if (rconsoleButton.isSelected()) {
createConsoleTabs();
appProtocol = RCONSOLE;
}
}
});
datalogButton.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
if (appProtocol == getAppProtocol()) return;
if (datalogButton.isSelected()) {
createDataLogTabs();
appProtocol = DATALOG;
}
}
});
// Create the panels
createNXTSelectionPanel();
createConsolePanel();
createDataPanel();
createMonitorPanel();
createControlPanel();
createMiscellaneousPanel();
// set the size of the files panel
filesPanel.setPreferredSize(filesPanelSize);
// Set up the frame
frame.setPreferredSize(frameSize);
createLCPTabs();
frame.add(tabbedPane);
frame.pack();
frame.setVisible(true);
}
/**
* Get files from the NXT and display them in the files panel
*/
private void showFiles() {
// Layout and populate files table
createFilesTable();
// Remove current content of files panel and recreate it
filesPanel.removeAll();
createFilesPanel();
// Recreate miscellaneous panel
otherPanel.removeAll();
createMiscellaneousPanel();
// Process buttons
// Delete Button: delete a file from the NXT
deleteButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
deleteFiles();
}
});
// Upload Button: upload a file to the NXT
uploadButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
upload();
}
});
// Download Button: download a file from from the NXT
downloadButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
download();
}
});
// Run Button: run a program on the NXT
runButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
runFile();
}
});
// Set Name Button: set a new name for the NXT
nameButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
rename(newName.getText());
}
});
// Sound button: Play Sound file
soundButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
playSoundFile();
}
});
// Format button; Delete user flash memory
formatButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
format();
}
});
// Pack the frame
//frame.pack();
}
/**
* Lay out NXT Selection panel
*/
private void createNXTSelectionPanel() {
JPanel nxtPanel = new JPanel();
nxtTablePane = new JScrollPane(nxtTable);
nxtTablePane.setPreferredSize(nxtTableSize);
nxtPanel.add(new JScrollPane(nxtTablePane), BorderLayout.WEST);
frame.getContentPane().add(nxtPanel, BorderLayout.NORTH);
nxtTable.setPreferredScrollableViewportSize(nxtButtonsPanelSize);
JLabel nameLabel = new JLabel("Name: ");
JPanel namePanel = new JPanel();
namePanel.add(nameLabel);
namePanel.add(nameText);
JPanel nxtButtonPanel = new JPanel();
nxtButtonPanel.add(namePanel);
JPanel buttonPanel = new JPanel();
buttonPanel.add(searchButton);
buttonPanel.add(connectButton);
nxtButtonPanel.add(buttonPanel);
nxtButtonPanel.add(usbButton);
nxtButtonPanel.add(bluetoothButton);
nxtButtonPanel.add(bothButton);
ButtonGroup protocolButtonGroup = new ButtonGroup();
protocolButtonGroup.add(usbButton);
protocolButtonGroup.add(bluetoothButton);
protocolButtonGroup.add(bothButton);
nxtButtonPanel.add(lcpButton);
nxtButtonPanel.add(rconsoleButton);
nxtButtonPanel.add(datalogButton);
ButtonGroup appProtocolButtonGroup = new ButtonGroup();
appProtocolButtonGroup.add(lcpButton);
appProtocolButtonGroup.add(rconsoleButton);
appProtocolButtonGroup.add(datalogButton);
nxtButtonPanel.setPreferredSize(nxtButtonsPanelSize);
nxtPanel.add(nxtButtonPanel, BorderLayout.EAST);
}
/**
* Lay out Console Panel
*/
private void createConsolePanel() {
JLabel consoleTitleLabel = new JLabel("Output from RConsole");
consolePanel.add(consoleTitleLabel);
consolePanel.add(new JScrollPane(theConsoleLog));
}
/**
* Lay out Data Console Panel
*/
private void createDataPanel() {
JLabel dataTitleLabel = new JLabel("Data Log");
dataPanel.add(dataTitleLabel, BorderLayout.NORTH);
dataPanel.add(new JScrollPane(theDataLog), BorderLayout.CENTER);
JPanel commandPanel = new JPanel();
commandPanel.add(new JLabel("Columns:"));
commandPanel.add(dataColumns);
commandPanel.add(dataDownloadButton);
dataPanel.add(commandPanel, BorderLayout.SOUTH);
}
/**
* Lay out Monitor Panel
*/
private void createMonitorPanel() {
JPanel leftPanel = new JPanel();
leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS));
JPanel batteryPanel = new JPanel();
batteryPanel.setBorder(etchedBorder);
batteryPanel.add(batteryGauge);
leftPanel.add(batteryPanel);
JPanel setSensorPanel = new JPanel();
setSensorPanel.setBorder(etchedBorder);
setSensorPanel.setLayout(new BoxLayout(setSensorPanel, BoxLayout.Y_AXIS));
JLabel setSensorLabel = new JLabel("Set Sensor type & mode");
JPanel labelPanel = new JPanel();
labelPanel.add(setSensorLabel);
setSensorPanel.add(labelPanel);
JPanel portPanel = new JPanel();
JLabel portLabel = new JLabel("Port:");
portPanel.add(portLabel);
portPanel.add(sensorList2);
setSensorPanel.add(portPanel);
JPanel typePanel = new JPanel();
JLabel typeLabel = new JLabel("Type:");
typePanel.add(typeLabel);
typePanel.add(sensorTypeList);
setSensorPanel.add(typePanel);
JPanel modePanel = new JPanel();
JLabel modeLabel = new JLabel("Mode:");
modePanel.add(modeLabel);
modePanel.add(sensorModeList);
setSensorPanel.add(modePanel);
JButton setSensorButton = new JButton("Set Sensor");
JPanel buttonPanel = new JPanel();
buttonPanel.add(setSensorButton);
setSensorPanel.add(buttonPanel);
leftPanel.add(setSensorPanel);
setSensorButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setSensor();
}
});
monitorPanel.add(leftPanel);
for (int i = 0; i < 4; i++) {
monitorPanel.add(sensorPanels[i]);
}
monitorPanel.add(monitorUpdateButton);
}
/**
* Create the tabs for LCP
*/
private void createLCPTabs() {
tabbedPane.removeAll();
tabbedPane.addTab("Files", filesPanel);
tabbedPane.addTab("Monitor", monitorPanel);
tabbedPane.addTab("Control", controlPanel);
tabbedPane.addTab("Miscellaneous", otherPanel);
}
/**
* Create the tabs for LCP
*/
private void createConsoleTabs() {
tabbedPane.removeAll();
tabbedPane.addTab("Console", consolePanel);
}
/**
* Create the tabs for LCP
*/
private void createDataLogTabs() {
tabbedPane.removeAll();
tabbedPane.addTab("Data Log", dataPanel);
}
/**
* Set up the files panel
*/
private void createFilesPanel() {
filesPanel.add(tablePane, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
buttonPanel.add(deleteButton);
buttonPanel.add(uploadButton);
buttonPanel.add(downloadButton);
buttonPanel.add(runButton);
buttonPanel.add(soundButton);
buttonPanel.add(formatButton);
buttonPanel.setPreferredSize(filesButtonsPanelSize);
filesPanel.add(buttonPanel, BorderLayout.SOUTH);
}
/**
* Populate the Other Panel
*/
private void createMiscellaneousPanel() {
createInfoPanel();
createTonePanel();
createI2cPanel();
createNamePanel();
}
/**
* Create rename NXT panel
*/
private void createNamePanel() {
JPanel namePanel = new JPanel();
namePanel.setPreferredSize(namePanelSize);
namePanel.setLayout(new BoxLayout(namePanel, BoxLayout.Y_AXIS));
namePanel.setBorder(etchedBorder);
JPanel titlePanel = new JPanel();
JLabel title = new JLabel("Change Friendly Name");
JPanel newNamePanel = new JPanel();
JLabel nameLabel = new JLabel("New name:");
titlePanel.add(title);
namePanel.add(titlePanel);
newNamePanel.add(nameLabel);
newNamePanel.add(newName);
namePanel.add(newNamePanel);
JPanel buttonPanel = new JPanel();
buttonPanel.add(nameButton);
namePanel.add(buttonPanel);
otherPanel.add(namePanel);
}
/**
* Create Info Panel
*/
private void createInfoPanel() {
JPanel infoPanel = new JPanel();
DeviceInfo di = null;
FirmwareInfo fi = null;
infoPanel.setLayout(new GridLayout(4, 2));
JLabel freeFlashLabel = new JLabel("Free flash: ");
String protocolVersionString = "Unknown";
String firmwareVersionString = "Unknown";
String freeFlashString = "Unknown";
if (nxtCommand != null) {
try {
di = nxtCommand.getDeviceInfo();
fi = nxtCommand.getFirmwareVersion();
protocolVersionString = fi.protocolVersion;
firmwareVersionString = fi.firmwareVersion;
freeFlashString = "" + di.freeFlash;
} catch (IOException ioe) {
showMessage("IO Exception getting device information");
}
}
JLabel freeFlash = new JLabel(freeFlashString);
infoPanel.add(freeFlashLabel);
infoPanel.add(freeFlash);
JLabel firmwareVersionLabel = new JLabel("Firmware version:");
JLabel firmwareVersion = new JLabel(firmwareVersionString);
infoPanel.add(firmwareVersionLabel);
infoPanel.add(firmwareVersion);
JLabel protocolVersionLabel = new JLabel("Protocol version:");
JLabel protocolVersion = new JLabel(protocolVersionString);
infoPanel.add(protocolVersionLabel);
infoPanel.add(protocolVersion);
infoPanel.setPreferredSize(innerInfoPanelSize);
JPanel outerInfoPanel = new JPanel();
outerInfoPanel.setPreferredSize(infoPanelSize);
outerInfoPanel.setBorder(etchedBorder);
JPanel headingPanel = new JPanel();
JLabel headingLabel = new JLabel("Brick Information");
headingPanel.add(headingLabel);
outerInfoPanel.add(headingPanel);
outerInfoPanel.add(infoPanel);
otherPanel.add(outerInfoPanel);
}
/**
* Create play tone panel
*/
private void createTonePanel() {
JPanel tonePanel = new JPanel();
tonePanel.setLayout(new BoxLayout(tonePanel, BoxLayout.Y_AXIS));
JPanel toneHeading = new JPanel();
JLabel toneLabel = new JLabel("Play tone");
toneHeading.add(toneLabel);
tonePanel.add(toneHeading);
JPanel freqPanel = new JPanel();
JLabel freqLabel = new JLabel("Frequency:");
freq.setColumns(5);
freqLabel.setLabelFor(freq);
JLabel durationLabel = new JLabel("Duration:");
duration.setColumns(5);
durationLabel.setLabelFor(duration);
JButton play = new JButton("Play tone");
freqPanel.add(freqLabel);
freqPanel.add(freq);
freqPanel.add(durationLabel);
freqPanel.add(duration);
tonePanel.add(freqPanel);
JPanel buttonPanel = new JPanel();
buttonPanel.add(play);
tonePanel.add(buttonPanel);
tonePanel.setPreferredSize(tonePanelSize);
tonePanel.setBorder(etchedBorder);
otherPanel.add(tonePanel);
play.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
playTone();
}
});
}
/**
* Create panel for I2C
*/
private void createI2cPanel() {
JPanel i2cPanel = new JPanel();
i2cPanel.setLayout(new BoxLayout(i2cPanel, BoxLayout.Y_AXIS));
JPanel labelPanel = new JPanel();
JLabel i2cTester = new JLabel("I2C Device Tester");
labelPanel.add(i2cTester);
i2cPanel.add(labelPanel);
JPanel topPanel = new JPanel();
JPanel sensorSelectPanel = new JPanel();
JLabel sensorLabel = new JLabel("Port:");
sensorSelectPanel.add(sensorLabel);
sensorSelectPanel.add(sensorList);
JLabel addressLabel = new JLabel("Address:");
JFormattedTextField address = new JFormattedTextField(new Integer(2));
address.setColumns(2);
sensorSelectPanel.add(addressLabel);
sensorSelectPanel.add(address);
topPanel.add(sensorSelectPanel);
JPanel rxlPanel = new JPanel();
JLabel rxlLabel = new JLabel("RxData length:");
rxlLabel.setLabelFor(rxDataLength);
rxlPanel.add(rxlLabel);
rxlPanel.add(rxDataLength);
topPanel.add(rxlPanel);
i2cPanel.add(topPanel);
txData.setColumns(32);
JPanel txPanel = new JPanel();
JLabel txLabel = new JLabel("Send (hex):");
txLabel.setLabelFor(txData);
txPanel.add(txLabel);
txPanel.add(txData);
i2cPanel.add(txPanel);
rxDataLength.setColumns(2);
JPanel rxPanel = new JPanel();
JLabel rxLabel = new JLabel("Received (hex):");
rxLabel.setLabelFor(rxData);
rxPanel.add(rxLabel);
rxPanel.add(rxData);
i2cPanel.add(rxPanel);
JButton txDataSend = new JButton("Send");
JButton i2cStatus = new JButton("Status");
JButton rxDataReceive = new JButton("Receive");
JPanel buttonsPanel = new JPanel();
buttonsPanel.add(txDataSend);
buttonsPanel.add(i2cStatus);
buttonsPanel.add(rxDataReceive);
i2cPanel.add(buttonsPanel);
i2cPanel.setBorder(BorderFactory.createEtchedBorder());
i2cPanel.setPreferredSize(i2cPanelSize);
otherPanel.add(i2cPanel);
txDataSend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
i2cSend();
}
});
rxDataReceive.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
i2cReceive();
}
});
i2cStatus.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
i2cStatus();
}
});
}
/**
* Set up the files table
*/
private void createFilesTable() {
fm = new ExtendedFileModel(nxtCommand);
table = new JTable(fm);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getColumnModel().getColumn(0).setPreferredWidth(fileNameColumnWidth);
tablePane = new JScrollPane(table);
tablePane.setPreferredSize(filesAreaSize);
}
/**
* Create a panel for motor control
*/
private JPanel createMotorPanel(int index) {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
final JSlider slider = new JSlider(0, 100);
sliders[index] = slider;
slider.setMajorTickSpacing(50);
slider.setMinorTickSpacing(10);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
JLabel label = new JLabel(" " + motorNames[index]);
label.setPreferredSize(labelSize);
panel.add(label);
final JLabel value = new JLabel(" " + slider.getValue());
value.setPreferredSize(labelSize);
panel.add(value);
slider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent c) {
value.setText(String.format("%6d", slider.getValue()));
}
});
slider.setPreferredSize(sliderSize);
panel.add(slider);
JLabel tacho = new JLabel("");
tacho.setPreferredSize(tachoSize);
tachos[index] = tacho;
panel.add(tacho);
JCheckBox selected = new JCheckBox();
selectors[index] = selected;
selected.setPreferredSize(labelSize);
panel.add(selected);
JCheckBox reverse = new JCheckBox();
reverse.setPreferredSize(labelSize);
reversors[index] = reverse;
panel.add(reverse);
JTextField limit = new JTextField(6);
limit.setMaximumSize(new Dimension(60, 20));
limits[index] = limit;
panel.add(limit);
JButton resetButton = new JButton("Reset");
resetButtons[index] = resetButton;
panel.add(resetButton);
resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
resetTacho((JButton) e.getSource());
}
});
panel.setBorder(BorderFactory.createEtchedBorder());
return panel;
}
/**
* Create the header line for the motors
*/
private JPanel createMotorsHeader() {
JPanel labelPanel = new JPanel();
JLabel motorLabel = new JLabel("Motor");
motorLabel.setPreferredSize(labelSize);
JLabel speedLabel = new JLabel("Speed");
speedLabel.setPreferredSize(labelSize);
JLabel sliderLabel = new JLabel(" Set speed");
sliderLabel.setPreferredSize(sliderSize);
JLabel tachoLabel = new JLabel("Tachometer");
tachoLabel.setPreferredSize(tachoSize);
JLabel selectedLabel = new JLabel("Selected");
selectedLabel.setPreferredSize(labelSize);
JLabel reverseLabel = new JLabel("Reverse");
reverseLabel.setPreferredSize(labelSize);
JLabel limitLabel = new JLabel("Limit");
limitLabel.setPreferredSize(labelSize);
JLabel resetLabel = new JLabel("Reset");
resetLabel.setPreferredSize(labelSize);
labelPanel.add(motorLabel);
labelPanel.add(speedLabel);
labelPanel.add(sliderLabel);
labelPanel.add(tachoLabel);
labelPanel.add(selectedLabel);
labelPanel.add(reverseLabel);
labelPanel.add(limitLabel);
labelPanel.add(resetLabel);
return labelPanel;
}
/**
* Create the control panel
*/
private void createControlPanel() {
JPanel motorsPanel = new JPanel();
motorsPanel.setLayout(new BoxLayout(motorsPanel, BoxLayout.Y_AXIS));
motorsPanel.add(createMotorsHeader());
for (int i = 0; i < 3; i++) {
motorsPanel.add(createMotorPanel(i));
}
JPanel buttonsPanel = new JPanel();
controlPanel.add(motorsPanel);
buttonsPanel.add(forwardButton);
buttonsPanel.add(backwardButton);
buttonsPanel.add(leftButton);
buttonsPanel.add(rightButton);
controlPanel.add(buttonsPanel);
forwardButton.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (!aMotorSelected()) return;
int[] speed = getSpeeds();
move(speed[0], speed[1], speed[2]);
}
public void mouseReleased(MouseEvent e) {
stopMotors();
}
});
backwardButton.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (!aMotorSelected()) return;
int[] speed = getSpeeds();
move(-speed[0], -speed[1], -speed[2]);
}
public void mouseReleased(MouseEvent e) {
stopMotors();
}
});
leftButton.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (!twoMotorsSelected()) return;
int[] speed = getSpeeds();
int[] multipliers = leftMultipliers();
move(speed[0] * multipliers[0], speed[1] * multipliers[1], speed[2] * multipliers[2]);
}
public void mouseReleased(MouseEvent e) {
stopMotors();
}
});
rightButton.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (!twoMotorsSelected()) return;
int[] speed = getSpeeds();
int[] multipliers = rightMultipliers();
move(speed[0] * multipliers[0], speed[1] * multipliers[1], speed[2] * multipliers[2]);
}
public void mouseReleased(MouseEvent e) {
stopMotors();
}
});
}
/**
* Return the number of motors selected
*/
private int numMotorsSelected() {
int numSelected = 0;
for(int i=0;i<3;i++) {
if (selectors[i].isSelected()) numSelected ++;
}
return numSelected;
}
/**
* Return true iff exactly two motors selected and show message if not
*/
private boolean twoMotorsSelected() {
if (numMotorsSelected() != 2) {
showMessage("Exactly two motors must be selected");
return false;
}
return true;
}
/**
* Return true iff at least one motor selected and show message if not
*/
private boolean aMotorSelected() {
if (numMotorsSelected() < 1) {
showMessage("At least one motor must be selected");
return false;
}
return true;
}
/**
* Calculate speed multipliers for turning left
*/
private int[] leftMultipliers() {
int[] multipliers = new int[3];
boolean firstFound = false, secondFound = false;
for(int i=0;i<3;i++) {
if (selectors[i].isSelected() && !firstFound) {
firstFound = true;
multipliers[i] = -1;
} else if (selectors[i].isSelected() && !secondFound) {
secondFound = true;
multipliers[i] = 1;
} else {
multipliers[i] = 0;
}
}
return multipliers;
}
/**
* Calculate the speed multipliers for turning right
*/
private int[] rightMultipliers() {
int[] multipliers = new int[3];
boolean firstFound = false, secondFound = false;
for(int i=0;i<3;i++) {
if (selectors[i].isSelected() && !firstFound) {
firstFound = true;
multipliers[i] = 1;
} else if (selectors[i].isSelected() && !secondFound) {
secondFound = true;
multipliers[i] = -1;
} else {
multipliers[i] = 0;
}
}
return multipliers;
}
/**
* Download a file from the NXT
*/
private void getFile(File file, String fileName, int size) {
FileOutputStream out = null;
int received = 0;
try {
out = new FileOutputStream(file);
} catch (FileNotFoundException e) {}
try {
FileInfo fi = nxtCommand.openRead(fileName);
do {
byte[] data = nxtCommand.readFile((byte) 0,
(size - received < 51 ? size - received : 51));
received += data.length;
out.write(data);
} while (received < size);
nxtCommand.closeFile(fi.fileHandle);
out.close();
} catch (IOException ioe) {
showMessage("IOException downloading file");
}
}
/**
* Show a pop-up message
*/
public void showMessage(String msg) {
JOptionPane.showMessageDialog(frame, msg);
}
/**
* Update the sensor dials
*/
private void updateSensors() {
if (nxtCommand == null) return;
for (int i = 0; i < 4; i++) {
int max = 1024;
sensorPanels[i].setRawVal(sensorValues[i].rawADValue);
if (sensorValues[i].sensorMode == (byte) PCTFULLSCALEMODE) max = 100;
else if (sensorValues[i].sensorMode == (byte) BOOLEANMODE) max = 1;
sensorPanels[i].setScaledMaxVal(max);
sensorPanels[i].setScaledVal(sensorValues[i].scaledValue);
sensorPanels[i].setType(sensorTypes[sensorValues[i].sensorType]);
sensorPanels[i].repaint();
}
batteryGauge.setVal(mv);
}
/**
* Clear the files tab.
*/
private void clearFiles() {
filesPanel.removeAll();
filesPanel.repaint();
}
/**
* Switch between NXTS in table of available NXTs
*/
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
int row = nxtTable.getSelectedRow();
if (row < 0) return;
if (nxts[row].connectionState != NXTConnectionState.DISCONNECTED &&
nxts[row].connectionState != NXTConnectionState.UNKNOWN) {
updateConnectButton(true);
if (nxts[row].connectionState == NXTConnectionState.LCP_CONNECTED) {
nxtCommand = nxtCommands[row];
showFiles();
}
if (nxts[row].connectionState == NXTConnectionState.DATALOG_CONNECTED) {
dvc = dvcs[row];
}
if (nxts[row].connectionState == NXTConnectionState.RCONSOLE_CONNECTED) {
cvc = cvcs[row];
}
} else {
updateConnectButton(false);
clearFiles();
}
}
}
/**
* Search for available NXTs and populate table with results.
*/
private void search() {
closeAll();
clearFiles();
updateConnectButton(false);
nxtTable.setModel(new NXTConnectionModel(null, 0));
nxts = conn.search(nameText.getText(), null, getProtocols());
if (nxts.length == 0) {
showMessage("No NXTS found");
return;
}
nm = new NXTConnectionModel(nxts, nxts.length);
nxtTable.setModel(nm);
TableColumn col = nxtTable.getColumnModel().getColumn(3);
col.setPreferredWidth(150);
nxtTable.setRowSelectionInterval(0, 0);
nxtTable.getSelectionModel().addListSelectionListener(control);
nxtCommands = new NXTCommand[nxts.length];
nxtComms = new NXTComm[nxts.length];
dvcs = new DataViewComms[nxts.length];
cvcs = new ConsoleViewComms[nxts.length];
}
/**
* Close all connections
*/
private void closeAll() {
if (nxtCommands == null) return;
for (int i = 0; i < nxtCommands.length; i++) {
NXTCommand nc = nxtCommands[i];
if (nc != null)
try {
nc.close();
} catch (IOException ioe) {}
}
nxtCommand = null;
}
/**
* Update connection status in the connections table
*/
private void updateConnectionStatus(int row, NXTConnectionState state) {
nm.setConnected(row, state);
nxtTable.repaint();
updateConnectButton(state != NXTConnectionState.DISCONNECTED);
if (state != NXTConnectionState.LCP_CONNECTED) nxtCommands[row] = null;
}
/**
* Toggle Connect button between Connect and Disconnect
*/
private void updateConnectButton(boolean connected) {
connectButton.setText((connected ? "Disconnect" : "Connect"));
}
/**
* Get the selected protocols
*/
private int getProtocols() {
int protocols = 0;
if (usbButton.isSelected()) protocols = NXTCommFactory.USB;
if (bluetoothButton.isSelected()) protocols = NXTCommFactory.BLUETOOTH;
if (bothButton.isSelected()) protocols = NXTCommFactory.USB | NXTCommFactory.BLUETOOTH;
return protocols;
}
/**
* Get the Application protocol
*/
private int getAppProtocol() {
int appProtocol = 0;
if (lcpButton.isSelected()) appProtocol = LCP;
if (rconsoleButton.isSelected()) appProtocol = RCONSOLE;
if (datalogButton.isSelected()) appProtocol = DATALOG;
return appProtocol;
}
/**
* Stop the motors on the NXT and update the tachometer values
*/
private void stopMotors() {
try {
if (nxtCommand == null) return;
nxtCommand.setOutputState(0, (byte) 0, 0, 0, 0, 0, 0);
nxtCommand.setOutputState(1, (byte) 0, 0, 0, 0, 0, 0);
nxtCommand.setOutputState(2, (byte) 0, 0, 0, 0, 0, 0);
tachos[0].setText(" " + nxtCommand.getTachoCount(0));
tachos[1].setText(" " + nxtCommand.getTachoCount(1));
tachos[2].setText(" " + nxtCommand.getTachoCount(2));
} catch (IOException ioe) {
showMessage("IOException while stopping motors");
}
}
/**
* Get an array of the tacho limit text boxes
*/
private int[] getLimits() {
int[] lim = new int[3];
for (int i = 0; i < 3; i++) {
try {
lim[i] = Integer.parseInt(limits[i].getText());
} catch (NumberFormatException nfe) {
lim[i] = 0;
}
}
return lim;
}
/**
* Get an array of the speed slider values
*/
private int[] getSpeeds() {
int[] speed = new int[3];
for (int i = 0; i < 3; i++) {
speed[i] = sliders[i].getValue();
if (reversors[i].isSelected()) speed[i] = -speed[i];
}
return speed;
}
/**
* Retrieve the sensor and battery values from the NXT
*/
private void getSensorValues() {
try {
for (int i = 0; i < 4; i++) {
if (nxtCommand == null) return;
sensorValues[i] = nxtCommand.getInputValues(i);
}
mv = nxtCommand.getBatteryLevel();
} catch (IOException ioe) {
System.err.println(ioe.getMessage());
}
}
/**
* Convert a byte array to a string of hex characters
*/
private String toHex(byte[] b) {
StringBuffer output = new StringBuffer();
for (int i = 0; i < b.length; i++) {
output.append(Integer.toHexString((int) b[i]));
}
return output.toString();
}
/**
* Convert a string of hex characters to a byte array
*/
private byte[] fromHex(String s) {
byte[] reply = new byte[s.length() / 2];
for (int i = 0; i < reply.length; i++) {
char c1 = s.charAt(i * 2);
char c2 = s.charAt(i * 2 + 1);
reply[i] = (byte) (getHexDigit(c1) << 4 | getHexDigit(c2));
}
return reply;
}
/**
* Convert a character to a hex digit
*/
private int getHexDigit(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return 0;
}
/**
* Add one byte array to another
*/
private byte[] appendBytes(byte[] array1, byte[] array2) {
byte[] array = new byte[array1.length + array2.length];
System.arraycopy(array1, 0, array, 0, array1.length);
System.arraycopy(array2, 0, array, array1.length, array2.length);
return array;
}
/**
* Connect to the NXT
*/
private void connect() {
int row = nxtTable.getSelectedRow();
int currentAppProtocol = getAppProtocol();
if (row >= 0) {
if (nxts[row].connectionState == NXTConnectionState.LCP_CONNECTED) {// Connected, so disconnect
try {
nxtCommand = nxtCommands[row];
nxtCommand.close();
nxts[row].connectionState = NXTConnectionState.DISCONNECTED;
} catch (IOException ioe) {
showMessage("IOException while disconnecting");
}
updateConnectionStatus(row, nxts[row].connectionState);
clearFiles();
nxtCommand = null;
return;
}
if (nxts[row].connectionState == NXTConnectionState.RCONSOLE_CONNECTED) {// Connected, so disconnect
cvc.close();
nxts[row].connectionState = NXTConnectionState.DISCONNECTED;
updateConnectionStatus(row, nxts[row].connectionState);
cvc = null;
return;
}
if (nxts[row].connectionState == NXTConnectionState.DATALOG_CONNECTED) {// Connected, so disconnect
dvc.close();
nxts[row].connectionState = NXTConnectionState.DISCONNECTED;
updateConnectionStatus(row, nxts[row].connectionState);
dvc.setConnected(false);
dvc = null;
return;
}
if (currentAppProtocol == RCONSOLE) {
consoleConnect();
return;
} else if (currentAppProtocol == DATALOG) {
dataConnect();
return;
}
// Connect
boolean open = false;
try {
clearFiles();
nxtCommand = new NXTCommand();
nxtCommands[row] = nxtCommand;
// currentRow = row;
NXTComm nxtComm = NXTCommFactory.createNXTComm(nxts[row].protocol);
nxtComms[row] = nxtComm;
open = nxtComm.open(nxts[row], NXTComm.LCP);
nxtCommand.setNXTComm(nxtComm);
//System.out.println("NXTInfo status " + nxts[row].connectionState);
} catch (NXTCommException e) {
open = false;
}
if (!open) {
showMessage("Failed to connect");
} else {
updateConnectionStatus(row, nxts[row].connectionState);
showFiles();
}
} else showMessage("You must do a search and select the NXT to connect to");
}
/**
* Connect to RConsole
*/
private void consoleConnect() {
int row = nxtTable.getSelectedRow();
if (row >= 0) {
boolean open = false;
theConsoleLog.setText("");
cvcs[row] = new ConsoleViewComms(this, true, false);
cvc = cvcs[row];
open = cvc.connectTo(nxts[row].name, nxts[row].deviceAddress, nxts[row].protocol);
if (!open) {
showMessage("Failed to connect to RConsole");
return;
}
nxts[row].connectionState = NXTConnectionState.RCONSOLE_CONNECTED;
updateConnectionStatus(row,nxts[row].connectionState);
}
}
/**
* Connect to the data logger
*/
private void dataConnect() {
int row = nxtTable.getSelectedRow();
if (row >= 0) {
boolean open = false;
theDataLog.setText("");
dvcs[row] = new DataViewComms(this);
dvc = dvcs[row];
open = dvc.connectTo(nxts[row].name, nxts[row].deviceAddress, nxts[row].protocol);
if (!open) {
showMessage("Failed to connect to data logger");
return;
}
nxts[row].connectionState = NXTConnectionState.DATALOG_CONNECTED;
updateConnectionStatus(row, nxts[row].connectionState);
}
}
/**
* Append data item to the data log
*/
public void append(float x) {
if (0 == recordCount % rowLength) theDataLog.append("\n");
theDataLog.append(FORMAT_FLOAT.format(x) + "\t ");
recordCount++;
}
/**
* Delete selected files
*/
private void deleteFiles() {
frame.setCursor(hourglassCursor);
try {
for (int i = 0; i < fm.getRowCount(); i++) {
Boolean b = (Boolean) fm.getValueAt(i, 4);
boolean deleteIt = b.booleanValue();
String fileName = (String) fm.getValueAt(i, 0);
if (deleteIt) {
fm.delete(fileName, i);
i--;
table.invalidate();
tablePane.revalidate();
}
}
} catch (IOException ioe) {
showMessage("IOException deleting files");
}
frame.setCursor(normalCursor);
}
/**
* Choose a file and update it
*/
private void upload() {
JFileChooser fc = new JFileChooser();
int returnVal = fc.showOpenDialog(frame);
if (returnVal == JFileChooser.APPROVE_OPTION) {
frame.setCursor(hourglassCursor);
try {
File file = fc.getSelectedFile();
if (file.getName().length() > 20) {
showMessage("File name is more than 20 characters");
} else {
nxtCommand.uploadFile(file, file.getName());
String msg = fm.fetchFiles();
if (msg != null) showMessage(msg);
table.invalidate();
tablePane.revalidate();
}
} catch (IOException ioe) {
showMessage("IOException uploading file");
}
frame.setCursor(normalCursor);
}
}
/**
* Download the selected file
*/
private void download() {
int i = table.getSelectedRow();
if (i < 0) return;
String fileName = fm.getFile(i).fileName;
int size = fm.getFile(i).fileSize;
JFileChooser fc = new JFileChooser();
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setSelectedFile(new File(fileName));
int returnVal = fc.showSaveDialog(frame);
if (returnVal == 0) {
File file = fc.getSelectedFile();
frame.setCursor(hourglassCursor);
getFile(file, fileName, size);
frame.setCursor(normalCursor);
}
}
/**
* Run the selected file.
*/
private void runFile() {
int row = table.getSelectedRow();
if (row < 0) return;
String fileName = fm.getFile(row).fileName;
try {
nxtCommand.startProgram(fileName);
nxtCommand.close();
nxtCommand = null;
updateConnectionStatus(nxtTable.getSelectedRow(), nxts[row].connectionState);
clearFiles();
} catch (IOException ioe) {
showMessage("IOException running program");
}
}
/**
* Change the friendly name of the NXT
*/
private void rename(String name) {
if (nxtCommand == null) return;
if (name != null && name.length() <= 16 && name.length() > 0) {
frame.setCursor(hourglassCursor);
try {
nxtCommand.setFriendlyName(name);
frame.setTitle(title + " : " + name);
newName.setText("");
} catch (IOException ioe) {
showMessage("IOException setting friendly name");
}
frame.setCursor(normalCursor);
} else showMessage("Please supply a name from 1 to 16 chareacters");
}
/**
* Move the motors
*/
private void move(int speed0, int speed1, int speed2 ) {
int[] lim = getLimits();
try {
if (nxtCommand == null) return;
if (selectors[0].isSelected())
nxtCommand.setOutputState(0, (byte) speed0, 0, 0, 0, 0, lim[0]);
if (selectors[1].isSelected())
nxtCommand.setOutputState(1, (byte) speed1, 0, 0, 0, 0, lim[1]);
if (selectors[2].isSelected())
nxtCommand.setOutputState(2, (byte) speed2, 0, 0, 0, 0, lim[2]);
} catch (IOException ioe) {
showMessage("IOException updating control");
}
}
/**
* Set the sensor type and mode
*/
private void setSensor() {
try {
if (nxtCommand == null) return;
nxtCommand.setInputMode(
sensorList.getSelectedIndex(),
sensorTypeValues[sensorTypeList.getSelectedIndex()],
sensorModeValues[sensorModeList.getSelectedIndex()]);
} catch (IOException ioe) {
showMessage("IOException setting sensor type");
}
}
/**
* Play a tone
*/
private void playTone() {
try {
if (nxtCommand == null) return;
nxtCommand.playTone((Integer) freq.getValue(), (Integer) duration.getValue());
} catch (IOException ioe) {
showMessage("IO Exception playing tone");
} catch (NumberFormatException nfe) {
showMessage("Frequency and Duration must be integers");
}
}
/**
* Reset the tachometer for a motor
*/
private void resetTacho(JButton b) {
int motor = -1;
for (int i = 0; i < 3; i++) {
if (b == resetButtons[i]) motor = i;
}
if (nxtCommand == null) return;
try {
nxtCommand.resetMotorPosition(motor, false);
tachos[motor].setText(" " + nxtCommand.getTachoCount(motor));
} catch (IOException ioe) {
showMessage("IO Exception resetting motor");
}
}
/**
* Play a sound file
*/
private void playSoundFile() {
int row = table.getSelectedRow();
if (row < 0) return;
String fileName = fm.getFile(row).fileName;
try {
nxtCommand.playSoundFile(fileName, false);
} catch (IOException ioe) {
showMessage("IO Exception playing sound file");
}
}
/**
* Send I2C request
*/
private void i2cSend() {
byte[] address = new byte[1];
address[0] = 2; // default I2C address
if (nxtCommand == null) return;
try {
nxtCommand.LSWrite(
(byte) sensorList.getSelectedIndex(),
appendBytes(address, fromHex(txData.getText())),
((Integer) rxDataLength.getValue()).byteValue());
} catch (IOException ioe) {
showMessage("IO Exception sending txData");
}
}
/**
* Send an i2c status request
*/
private void i2cStatus() {
if (nxtCommand == null) return;
try {
byte[] reply = nxtCommand.LSGetStatus((byte) sensorList.getSelectedIndex());
if (reply != null) {
System.out.println("LSStatus reply length = " + reply.length);
String hex = toHex(reply);
rxData.setText(hex);
} else
rxData.setText("null");
} catch (IOException ioe) {
showMessage("IO Exception getting status");
}
}
/**
* Read i2c reply
*/
private void i2cReceive() {
if (nxtCommand == null) return;
try {
byte[] reply = nxtCommand.LSRead((byte) sensorList.getSelectedIndex());
if (reply != null) {
String hex = toHex(reply);
rxData.setText(hex);
} else
rxData.setText("null");
} catch (IOException ioe) {
showMessage("IO Exception reading rxData");
}
}
/**
* Format the file system
*/
private void format() {
if (nxtCommand == null) return;
try {
nxtCommand.deleteUserFlash();
fm.fetchFiles();
table.invalidate();
tablePane.revalidate();
} catch (IOException ioe) {
showMessage("IO Exception formatting file system");
}
}
public void logMessage(String msg) {
System.out.println(msg);
}
public void connectedTo(String name, String address) {
}
public void setStatus(String msg) {
}
public void append(String value) {
theConsoleLog.append(value);
}
public void updateLCD(byte[] buffer)
{
}
}