Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / colonet / client / Colonet.java @ 555

History | View | Annotate | Download (43.1 KB)

1
//
2
//        Colonet.java
3
//
4

    
5
import javax.swing.*;
6
import javax.swing.event.*;
7
import javax.imageio.*;
8
import java.awt.*;
9
import java.awt.image.*;
10
import java.awt.event.*;
11
import java.net.*;
12
import java.io.*;
13
import java.util.*;
14

    
15
/**
16
* The Colonet Graphical User Interface Applet for use locally and over an internet connection.
17
* @author Gregory Tress
18
*
19
* To generate javadoc on this file or other java files, use javadoc *.java -d doc, where doc
20
* is the name of the folder into which the files should be written.
21
*/
22
public class Colonet extends JApplet implements ActionListener, MouseInputListener, KeyListener, Runnable {
23

    
24
        // Used for images
25
        final int CANVAS_SIZE = 500;        //the applet may be slow if the canvas gets too large
26
        final int BUFFER = 50;
27
        final int RADIUS = 30;
28

    
29
        //Used for the robot controller
30
        final int VECTOR_CONTROLLER_HEIGHT = 220;
31
        final int VECTOR_CONTROLLER_WIDTH = 350;
32

    
33
        // Connection
34
        JTextField txtHost;
35
        JTextField txtPort;
36
        JButton btnConnect;
37
        JButton btnGetXBeeIDs;
38
        JLabel lblConnectionStatus;
39
        JTextArea txtMatrix;
40
        JTextArea txtInfo;
41
        JPanel panelConnect;
42
        JPanel panelServerInterface;
43
        Socket socket;
44
        OutputStreamWriter out;
45
        DataUpdater dataUpdater;
46

    
47
        // South
48
        JPanel panelSouth;
49
        JTextArea log;
50
        JScrollPane spLog;
51

    
52
        // Control
53
        JPanel panelControl;
54
        JTabbedPane tabPaneControl;
55
        JPanel panelRobotControl;
56
        JPanel panelRobotDirection;
57
        JPanel panelRobotDirectionButtons;
58
        JPanel panelRobotCommands;
59
        JButton btnF, btnB, btnL, btnR, btnActivate;
60
        JComboBox cmbRobotNum;
61
        JLabel lblBattery;
62
        JLabel lblSelected;
63
        BatteryIcon batteryIcon;
64
        JPanel panelBattery;
65
        VectorController vectorController;
66
        BufferedImage imageVectorControl;
67
        JButton btnAssignID;
68
        boolean setWaypoint;
69
        int setWaypointID;
70
        JButton btnCommand_MoveTo;
71
        JButton btnCommand_MoveAll;
72
        JButton btnCommand_StopTask;
73
        JButton btnCommand_ResumeTask;
74
        JButton btnCommand_ChargeNow;
75
        JButton btnCommand_StopCharging;
76

    
77
        // Task Manager
78
        JPanel panelTaskManager;
79
        JScrollPane spTaskManager;
80
        JPanel panelTaskManagerControls;
81
        JPanel panelTaskManagerControlsPriority;
82
        DefaultListModel taskListModel;
83
        JList taskList;
84
        JButton btnAddTask;
85
        JButton btnRemoveTask;
86
        JButton btnMoveTaskUp;
87
        JButton btnMoveTaskDown;
88
        JButton btnUpdateTasks;
89
        TaskAddWindow taskAddWindow;
90

    
91
        //Webcam
92
        WebcamPanel panelWebcam;
93
        GraphicsPanel panelGraph;
94
        GraphicsConfiguration gc;
95
        volatile BufferedImage image;
96
        volatile Graphics2D canvas;
97
        int cx, cy;
98
        JTabbedPane tabPaneMain;
99

    
100
        Font botFont;
101
        volatile int numBots;
102
        volatile int selectedBot;         //the user has selected this bot graphically
103
        volatile java.util.List <RobotIcon> robotIcons;         //contains boundary shapes around bots for click detection
104
        volatile int[] xbeeID;
105

    
106
        Colonet self = this;
107
        Thread paintThread;
108
        WebcamLoader webcamLoader;
109
        volatile ColonetServerInterface csi;
110

    
111
        public void init () {
112
                // Set the default look and feel - choose one
113
                //String laf = UIManager.getSystemLookAndFeelClassName();
114
                String laf = UIManager.getCrossPlatformLookAndFeelClassName();
115
                //String laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
116
                try {
117
                        UIManager.setLookAndFeel(laf);
118
                } catch (UnsupportedLookAndFeelException exc) {
119
                        System.err.println ("Warning: UnsupportedLookAndFeel: " + laf);
120
                } catch (Exception exc) {
121
                        System.err.println ("Error loading " + laf + ": " + exc);
122
                }
123

    
124
                // We should invoke and wait to avoid browser display difficulties
125
                Runnable r = new Runnable() {
126
                        public void run() {
127
                                createAndShowGUI();
128
                        }
129
                };
130

    
131
                try {
132
                        SwingUtilities.invokeAndWait(r);
133
                } catch (InterruptedException e) {
134
                        //Not really sure why we would be in this situation
135
                        System.out.println("InterruptedException in init: " + e);
136
                } catch (java.lang.reflect.InvocationTargetException e) {
137
                        //This could happen for various reasons if there is a problem in createAndShowGUI
138
                        e.printStackTrace();
139
                }
140
        }
141

    
142
        public void destroy () {
143
                try { paintThread.interrupt(); } catch (Exception e) { }
144
        }
145

    
146
        private synchronized void createAndShowGUI () {
147
                // init graphical elements
148
                // Get the graphics configuration of the screen to create a buffer
149
                gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
150
                image = gc.createCompatibleImage(CANVAS_SIZE,CANVAS_SIZE);
151
                canvas = image.createGraphics();
152
                canvas.setStroke(new BasicStroke(2));         //set pen width
153
                panelGraph = new GraphicsPanel(image, false);         //set automatic double-buffering to false. we are doing it manually.
154
                panelWebcam = new WebcamPanel();
155
                tabPaneMain = new JTabbedPane();
156
                tabPaneMain.setFont(new Font("arial", Font.PLAIN, 16));
157
                tabPaneMain.add(panelWebcam, "Webcam");
158
                //tabPaneMain.add(panelGraph, "Graph");
159

    
160
                // Calculate center of canvas
161
                cx = image.getWidth() / 2;
162
                cy = image.getHeight() / 2;
163

    
164
                // Set up robots
165
                botFont = new Font("arial", Font.PLAIN, 14);
166
                numBots = 0;
167
                selectedBot = -1;
168
                robotIcons = new ArrayList <RobotIcon> ();
169

    
170
                // Connection area
171
                txtMatrix = new JTextArea();
172
                txtMatrix.setBorder(BorderFactory.createTitledBorder("Info"));
173
                txtInfo = new JTextArea();
174
                txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
175
                txtInfo.setEditable(false);
176
                txtHost = new JTextField(this.getDocumentBase().getHost());
177
                txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
178
                txtPort = new JTextField("10123");
179
                txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
180
                btnConnect = new JButton("Connect");
181
                btnConnect.setFont(new Font("arial", Font.BOLD, 16));
182
                btnGetXBeeIDs = new JButton("Get XBee IDs");
183
                getRootPane().setDefaultButton(btnConnect);
184
                lblConnectionStatus = new JLabel("Status: Offline");
185
                panelConnect = new JPanel();
186
                panelConnect.setLayout(new GridLayout(6,1));
187
                panelConnect.add(lblConnectionStatus);
188
                panelConnect.add(txtHost);
189
                panelConnect.add(txtPort);
190
                panelConnect.add(btnConnect);
191
                //panelConnect.add(btnGetXBeeIDs);
192
                panelServerInterface = new JPanel();
193
                panelServerInterface.setLayout(new GridLayout(2,1));
194
                panelServerInterface.add(panelConnect);
195
                panelServerInterface.add(txtMatrix);
196

    
197
                // Robot direction panel
198
                panelRobotDirection = new JPanel();
199
                panelRobotDirectionButtons = new JPanel();
200

    
201

    
202
                Font f = new Font(null, Font.PLAIN, 18);
203
                btnF = new JButton("\u2191");
204
                btnF.setFont(f);
205
                btnB = new JButton("\u2193");
206
                btnB.setFont(f);
207
                btnL = new JButton("\u2190");
208
                btnL.setFont(f);
209
                btnR = new JButton("\u2192");
210
                btnR.setFont(f);
211

    
212
                btnActivate = new JButton("\u25A0");
213
                btnActivate.setFont(new Font(null, Font.BOLD, 36));
214

    
215
                panelRobotDirectionButtons.setLayout(new GridLayout(1,5));
216
                panelRobotDirectionButtons.add(btnActivate);
217
                panelRobotDirectionButtons.add(btnF);
218
                panelRobotDirectionButtons.add(btnB);
219
                panelRobotDirectionButtons.add(btnL);
220
                panelRobotDirectionButtons.add(btnR);
221

    
222
                imageVectorControl = gc.createCompatibleImage(VECTOR_CONTROLLER_WIDTH, VECTOR_CONTROLLER_HEIGHT);
223
                vectorController = new VectorController(imageVectorControl);
224
                panelRobotDirection.setLayout(new BorderLayout());
225
                panelRobotDirection.add(vectorController, BorderLayout.CENTER);
226
                panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
227

    
228
                // Robot Control and Commands
229
                panelRobotCommands = new JPanel();
230
                panelRobotCommands.setLayout(new GridLayout(5,2));
231
                cmbRobotNum = new JComboBox();
232
                // Battery subset
233
                batteryIcon = new BatteryIcon(0);
234
                lblBattery = new JLabel(batteryIcon);
235
                lblSelected = new JLabel("None");
236
                // Command subset
237
                setWaypoint = false;
238
                setWaypointID = -1;
239
                btnAssignID = new JButton("Assign ID");
240
                btnCommand_MoveTo = new JButton("Move to ...");
241
                btnCommand_MoveAll = new JButton("Move all ...");
242
                btnCommand_StopTask = new JButton("Stop Current Task");
243
                btnCommand_ResumeTask = new JButton("Resume Current Task");
244
                btnCommand_ChargeNow = new JButton("Recharge Now");
245
                btnCommand_StopCharging = new JButton("Stop Recharging");
246
                panelRobotCommands.add(new JLabel("Select Robot to Control: "));
247
                panelRobotCommands.add(cmbRobotNum);
248
                panelRobotCommands.add(new JLabel("Battery Level: "));
249
                panelRobotCommands.add(lblBattery);
250
                panelRobotCommands.add(new JLabel("Selected Icon: "));
251
                panelRobotCommands.add(lblSelected);
252
                panelRobotCommands.add(btnAssignID);
253
                panelRobotCommands.add(new JLabel(""));
254
                panelRobotCommands.add(btnCommand_MoveTo);
255
                panelRobotCommands.add(btnCommand_MoveAll);
256
                //panelRobotCommands.add(btnCommand_StopTask);
257
                //panelRobotCommands.add(btnCommand_ResumeTask);
258
                //panelRobotCommands.add(btnCommand_ChargeNow);
259
                //panelRobotCommands.add(btnCommand_StopCharging);
260
                panelRobotControl = new JPanel();
261
                panelRobotControl.setLayout(new GridLayout(2,1));
262
                panelRobotControl.add(panelRobotDirection);
263
                panelRobotControl.add(panelRobotCommands);
264

    
265

    
266
                // Task Manager
267
                panelTaskManager = new JPanel();
268
                panelTaskManager.setLayout(new BorderLayout());
269
                taskListModel = new DefaultListModel();
270
                taskList = new JList(taskListModel);
271
                taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
272
                taskList.setSelectedIndex(0);
273
                spTaskManager = new JScrollPane(taskList);
274
                panelTaskManagerControls = new JPanel();
275
                panelTaskManagerControls.setLayout(new GridLayout(1,4));
276
                panelTaskManagerControlsPriority = new JPanel();
277
                panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
278
                btnAddTask = new JButton("Add...");
279
                btnRemoveTask = new JButton("Remove");
280
                btnMoveTaskUp = new JButton("^");
281
                btnMoveTaskDown = new JButton("v");
282
                btnUpdateTasks = new JButton("Update");
283
                panelTaskManagerControlsPriority.add(btnMoveTaskUp);
284
                panelTaskManagerControlsPriority.add(btnMoveTaskDown);
285
                panelTaskManagerControls.add(btnAddTask);
286
                panelTaskManagerControls.add(btnRemoveTask);
287
                panelTaskManagerControls.add(btnUpdateTasks);
288
                panelTaskManagerControls.add(panelTaskManagerControlsPriority);
289
                panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
290
                panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
291
                panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
292
                taskAddWindow = new TaskAddWindow();
293

    
294
                // Message log
295
                log = new JTextArea();
296
                spLog = new JScrollPane(log, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
297
                        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
298
                spLog.setBorder(BorderFactory.createTitledBorder("Log"));
299
                spLog.setPreferredSize(new Dimension(0, 120));
300
                log.setEditable(false);
301

    
302
                // Main control mechanism
303
                panelControl = new JPanel();
304
                panelControl.setLayout(new GridLayout(1,1));
305
                tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
306
                tabPaneControl.setFont(new Font("arial", Font.PLAIN, 16));
307

    
308
                tabPaneControl.setPreferredSize(new Dimension(VECTOR_CONTROLLER_WIDTH, 0));
309
                tabPaneControl.addTab("Connection", panelServerInterface);
310
                tabPaneControl.addTab("Robots", panelRobotControl);
311
                //tabPaneControl.addTab("Tasks", panelTaskManager);
312
                panelControl.add(tabPaneControl);
313

    
314
                // Set up elements in the south
315
                panelSouth = new JPanel();
316
                panelSouth.setLayout(new GridLayout(1,2));
317
                //panelSouth.add(spLog);
318

    
319
                // Put all elements in the ContentPane
320
                this.getContentPane().setLayout(new BorderLayout());
321
                this.getContentPane().add(tabPaneMain, BorderLayout.CENTER);
322
                this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
323
                this.getContentPane().add(panelControl, BorderLayout.EAST);
324
                this.setVisible(true);
325

    
326
                /* Add all listeners here */
327
                // Task Management
328
                btnAddTask.addActionListener(this);
329
                btnRemoveTask.addActionListener(this);
330
                btnMoveTaskUp.addActionListener(this);
331
                btnMoveTaskDown.addActionListener(this);
332
                btnUpdateTasks.addActionListener(this);
333
                // Robot Control
334
                btnF.addActionListener(this);
335
                btnB.addActionListener(this);
336
                btnL.addActionListener(this);
337
                btnR.addActionListener(this);
338
                btnF.addKeyListener(this);
339
                btnB.addKeyListener(this);
340
                btnL.addKeyListener(this);
341
                btnR.addKeyListener(this);
342
                btnActivate.addActionListener(this);
343
                btnActivate.addKeyListener(this);
344
                cmbRobotNum.addKeyListener(this);
345
                btnCommand_MoveTo.addActionListener(this);
346
                btnCommand_MoveAll.addActionListener(this);
347
                btnCommand_StopTask.addActionListener(this);
348
                btnCommand_ResumeTask.addActionListener(this);
349
                btnCommand_ChargeNow.addActionListener(this);
350
                btnCommand_StopCharging.addActionListener(this);
351
                // Other
352
                btnConnect.addActionListener(this);
353
                btnGetXBeeIDs.addActionListener(this);
354
                btnAssignID.addActionListener(this);
355
                panelWebcam.addMouseListener(this);
356
        }
357

    
358
        public void run () {
359
                while (true) {
360
                        repaint();
361
                        try {
362
                                Thread.sleep(90);
363
                        } catch (InterruptedException e) {
364
                                return;
365
                        }
366
                }
367
        }
368

    
369
        public void paint (Graphics g) {
370
                super.paint(g);
371
        }
372

    
373
        public void update (Graphics g) {
374
                paint(g);
375
        }
376

    
377
        /**
378
        * Gets the JTextArea used for storing the activity log. This method returns a reference to the
379
        * JTextArea that stores the log. The log can contain any activity that is revelant to the use
380
        * of the applet, and may optionally display debugging information.
381
        *
382
        * @return the JTextArea where BOM matrix information is stored.
383
        */
384
        public JTextArea getLog () {
385
                return log;
386
        }
387

    
388
        /**
389
        * Gets the JTextArea used for storing the BOM matrix data. This method returns a reference to the
390
        * JTextArea that stores the BOM matrix. The values in the matrix are stored as integers separated
391
        * by spaces, and the lines should be separated by a newline.
392
        *
393
        * @return the JTextArea where BOM matrix information is stored.
394
        */
395
        public JTextArea getMatrixInput () {
396
                return txtMatrix;
397
        }
398

    
399
        /**
400
        * Parses a String containing BOM matrix information.
401
        * The ColonetServerInterface receives lines of the BOM matrix.        (For encoding
402
        * information, see the ColonetServerInterface documentation.)         The entire matrix is passed
403
        * to the client when requested. This method takes a string of the form
404
        * "[command code] [command code] [number of robots] [data0] [data1] ..."
405
        * with tokens separated by spaces and containing no brackets.
406
        * The [command code]s are predefined values identifying this String as a BOM data
407
        * String, [number of robots] is an integer, and the values that follow are
408
        * the sensor readings of the robots in order, starting with robot 0.        Only [number of robots]^2
409
        * data entries will be read.        The matrix values are saved locally until the next String is parsed.
410
        *
411
        *
412
        * @param line the String containing BOM matrix information.
413
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots]^2 data entries in the String
414
        */
415
        public void parseMatrix (String line) {
416
                txtMatrix.setText("");
417
                String [] str = line.split(" ");
418
                int num = Integer.parseInt(str[2]);
419
                for (int i = 0; i < num; i++) {
420
                        for (int j = 0; j < num; j++) {
421
                                String next = str[3 + i*num + j];
422
                                if (next.equals("-1")) {
423
                                        txtMatrix.append("-");
424
                                } else {
425
                                        txtMatrix.append(next);
426
                                }
427

    
428
                                if (j < num - 1) {
429
                                        txtMatrix.append(" ");
430
                                }
431
                        }
432

    
433
                        if (i < num - 1) {
434
                                txtMatrix.append("\n");
435
                        }
436
                }
437
                repaint();
438
        }
439

    
440
        public void connect () {
441
                if (csi != null)
442
                        return;
443
                csi = new ColonetServerInterface(this);
444
                csi.connect(txtHost.getText(), txtPort.getText());
445
                if (!csi.isReady()) {
446
                        csi = null;
447
                        return;
448
                }
449
                webcamLoader = new WebcamLoader(this);
450
                dataUpdater = new DataUpdater();
451
                dataUpdater.start();
452
                webcamLoader.start();
453
                btnConnect.setText("Disconnect");
454
                lblConnectionStatus.setText("Status: Connected");
455
        }
456

    
457
        public void disconnect () {
458
                lblConnectionStatus.setText("Status: Disconnecting...");
459
                btnConnect.setText("Connect");
460
                lblConnectionStatus.setText("Status: Disconnected");
461
                dataUpdater.interrupt();
462
                csi = null;
463
        }
464

    
465
        /**
466
        * Parses a String containing a task queue update.
467
        * Format is currently not specified.
468
        * This method currently does nothing.
469
        *
470
        * @param line the String containing task queue update information.
471
        */
472
        public void parseQueue (String line) {
473
                log.append("Got queue update\n");
474
                //TODO: display new queue data in tasks tab
475
        }
476

    
477
        /**
478
        * Parses a String containing XBee ID values.
479
        * The ColonetServerInterface receives Strings of XBee information.        (For encoding
480
        * information, see the ColonetServerInterface documentation.)         This method takes
481
        * a string of the form "[command code] [command code] [number of robots] [id0] [id1] ..."
482
        * with tokens separated by spaces and containing no brackets.
483
        * The [command code]s are predefined values identifying this String as an XBee
484
        * ID String, [number of robots] is an integer, and the values that follow are
485
        * the IDs of the robots in order, starting with robot 0.        Only [number of robots]
486
        * will be read.         The ID values are saved locally until the next String is parsed.
487
        * The purpose of having this list is to ensure that robots are properly identified for control purposes.
488
        * This keeps robot identification consistent between sessions and prevents arbitrary assignment.
489
        *
490
        * @param line the String containing XBee ID information.
491
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots] IDs in the String
492
        * @see ColonetServerInterface#sendXBeeIDRequest()
493
        */
494
        public void parseXBeeIDs (String line) {
495
                String [] str = line.split(" ");
496
                int num = Integer.parseInt(str[2]);
497
                xbeeID = new int[num];
498
                for (int i = 0; i < num; i++) {
499
                        xbeeID[i] = Integer.parseInt(str[i+3]);
500
                }
501

    
502
                //update the list of robots to control
503
                //but save the old value first
504
                Object oldSelection = cmbRobotNum.getSelectedItem();
505
                cmbRobotNum.removeAllItems();
506
                cmbRobotNum.addItem(new String("         All         "));
507
                for (int i = 0; i < num; i++) {
508
                        cmbRobotNum.addItem(new String("" + xbeeID[i]));
509
                }
510
                cmbRobotNum.setSelectedItem(oldSelection);
511
                repaint();
512
        }
513

    
514
        /**
515
        * Parses a String containing battery information.
516
        * The ColonetServerInterface receives Strings of battery information.         (For encoding
517
        * information, see the ColonetServerInterface documentation.)         This method takes
518
        * a string of the form "[command code] [command code] [robot ID] [value]"
519
        * with tokens separated by spaces and containing no brackets.
520
        * The [command code]s are predefined values identifying this String as a battery
521
        * information String, [robot ID] is an integer, and [value] is a battery measurement.
522
        * This updates the batery information for a single robot.
523
        *
524
        *
525
        * @param line the String containing battery information.
526
        * @see ColonetServerInterface#sendBatteryRequest(int)
527
        */
528
        public void parseBattery (String line) {
529
                String [] str = line.split(" ");
530
                int botNum = Integer.parseInt(str[2]);
531
                int level = Integer.parseInt(str[3]);
532
                int selected = -1;
533
                try {
534
                        selected = Integer.parseInt((String)cmbRobotNum.getSelectedItem());
535
                } catch (Exception e) {
536
                        System.out.println("Exception in parseBattery.");
537
                }
538

    
539
                if (selected == botNum) {
540
                        batteryIcon.setLevel(level);
541
                }
542
                repaint();
543
        }
544

    
545
        /**
546
        * Parses a String containing visual robot position information along with
547
        * canonical ID assignments.
548
        */
549
        public void parsePositions (String line) {
550
                String [] str = line.split(" ");
551
                java.util.List <RobotIcon> newList = new ArrayList <RobotIcon> ();
552

    
553
                for (int i = 2; i < str.length; i+=3) {
554
                        int id = Integer.parseInt(str[i]);
555
                        int x = Integer.parseInt(str[i+1]);
556
                        int y = Integer.parseInt(str[i+2]);
557
                        RobotIcon newIcon = new RobotIcon(id, x, y);
558
                        if (newIcon.id >= 0) {
559
                                newIcon.color = Color.GREEN;
560
                        }
561
                        newList.add(newIcon);
562
                }
563
                robotIcons = newList;
564
                repaint();
565
        }
566

    
567
        //
568
        // MouseListener methods
569
        //
570
        public void mousePressed(MouseEvent e) {
571
                //Start a new Thread to handle the MouseEvent
572
                (new MouseHandler(e)).start();
573
        }
574
        public void mouseExited(MouseEvent e) {
575
        }
576
        public void mouseEntered(MouseEvent e) {
577
        }
578
        public void mouseReleased(MouseEvent e) {
579
        }
580
        public void mouseClicked(MouseEvent e) {
581
        }
582
        public void mouseDragged(MouseEvent e) {
583
        }
584
        public void mouseMoved(MouseEvent e) {
585
        }
586

    
587
        //
588
        // KeyListener methods
589
        //
590
        public void keyPressed (KeyEvent e) {
591
                //Start a new Thread to handle the KeyEvent
592
                (new KeyHandler(e)).start();
593
        }
594
        public void keyReleased (KeyEvent e) {
595
        }
596
        public void keyTyped (KeyEvent e) {
597
        }
598

    
599
        //
600
        // ActionListener method
601
        //
602
        public void actionPerformed (ActionEvent e) {
603
                // Start a new Thread to handle the ActionEvent
604
                (new ActionHandler(e)).start();
605
        }
606

    
607
        class MouseHandler extends Thread {
608
                MouseEvent e;
609

    
610
                public MouseHandler (MouseEvent event) {
611
                        super("MouseHandler");
612
                        this.e = event;
613
                }
614

    
615
                public void run () {
616
                        Point pt = panelWebcam.convertClick(e);
617

    
618
                        // If we are selecting a waypoint (destination) for a specific bot
619
                        if (setWaypoint && setWaypointID        >= 0) {
620
                                setWaypoint = false;
621
                                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
622
                                if (selectedBot < 0) {
623
                                        return;
624
                                }
625

    
626
                                for (int i = 0; i < robotIcons.size(); i++) {
627
                                        RobotIcon r = robotIcons.get(i);
628
                                        if (r.id == selectedBot) {
629
                                                r.destx = pt.x;
630
                                                r.desty = pt.y;
631
                                                if (csi != null) {
632
                                                        csi.sendAbsoluteMove(r.id, r.destx, r.desty);
633
                                                }
634
                                        }
635
                                }
636

    
637
                                return;
638
                        }
639

    
640
                        // Right-click also means we are moving a robot
641
                        if (e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3) {
642
                                if (selectedBot < 0) {
643
                                        return;
644
                                }
645

    
646
                                for (int i = 0; i < robotIcons.size(); i++) {
647
                                        RobotIcon r = robotIcons.get(i);
648
                                        if (r.id == selectedBot) {
649
                                                r.destx = pt.x;
650
                                                r.desty = pt.y;
651
                                                if (csi != null) {
652
                                                        csi.sendAbsoluteMove(r.id, r.destx, r.desty);
653
                                                }
654
                                        }
655
                                }
656

    
657
                                return;
658
                        }
659

    
660
                        // If we are setting all waypoints
661
                        if (setWaypoint) {
662
                                setWaypoint = false;
663
                                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
664
                                for (int i = 0; i < robotIcons.size(); i++) {
665
                                        RobotIcon r = robotIcons.get(i);
666
                                        r.destx = pt.x;
667
                                        r.desty = pt.y;
668
                                }
669
                                
670
                                return;
671
                        }
672

    
673
                        // Otherwise, we are selecting a bot, or doing nothing
674
                        for (int i = 0; i < robotIcons.size(); i++) {
675
                                RobotIcon r = robotIcons.get(i);
676
                                if (r.contains(pt.x, pt.y)) {
677
                                        selectedBot = r.id;
678
                                        lblSelected.setText("" + r.id);
679
                                        // Try to select the clicked bot, if its XBee ID is detected.
680
                                        for (int j = 1; j < cmbRobotNum.getItemCount(); j++) {
681
                                                if (Integer.parseInt(cmbRobotNum.getItemAt(j).toString()) == selectedBot) {
682
                                                        cmbRobotNum.setSelectedIndex(j);
683
                                                }
684
                                        }
685
                                        return;
686
                                }
687
                        }
688

    
689
                        repaint();
690
                }
691
        }
692

    
693
        class KeyHandler extends Thread {
694
                KeyEvent e;
695

    
696
                public KeyHandler (KeyEvent event) {
697
                        super("KeyHandler");
698
                        this.e = event;
699
                }
700

    
701
                public void run () {
702
                        int code = e.getKeyCode();
703
                        if (code == KeyEvent.VK_UP) {
704
                                vectorController.setMaxForward();
705
                                vectorController.sendToServer();
706
                        } else if (code == KeyEvent.VK_DOWN) {
707
                                vectorController.setMaxReverse();
708
                                vectorController.sendToServer();
709
                        } else if (code == KeyEvent.VK_LEFT) {
710
                                vectorController.setMaxLeft();
711
                                vectorController.sendToServer();
712
                        } else if (code == KeyEvent.VK_RIGHT) {
713
                                vectorController.setMaxRight();
714
                                vectorController.sendToServer();
715
                        } else if (code == KeyEvent.VK_S) {
716
                                vectorController.setZero();
717
                                vectorController.sendToServer();
718
                        }
719
                        repaint();
720
                }
721
        }
722

    
723
        class ActionHandler extends Thread {
724
                ActionEvent e;
725

    
726
                public ActionHandler (ActionEvent event) {
727
                        super("ActionHandler");
728
                        this.e = event;
729
                }
730

    
731
                public void run () {
732
                        Object source = e.getSource();
733

    
734
                        // General Actions
735
                        if (source == btnConnect) {
736
                                if (csi == null) {
737
                                        connect();
738
                                } else {
739
                                        disconnect();
740
                                }
741
                        } else if (source == btnGetXBeeIDs) {
742
                                csi.sendXBeeIDRequest();
743
                        } else if (source == btnAssignID) {
744
                                String message;
745
                                int curID = selectedBot;
746
                                if (curID < 0) {
747
                                        message = "That robot is unidentified. Please specify its ID.";
748
                                } else {
749
                                        message = "That robot has ID " + curID + ". You may reassign it now.";
750
                                }
751
                                String result = JOptionPane.showInputDialog(self, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
752
                                if (result == null) {
753
                                        return;
754
                                }
755
                                int newID = -1;
756
                                try {
757
                                        newID = Integer.parseInt(result);
758
                                } catch (Exception ex) {
759
                                        csi.warn("Invalid ID.");
760
                                        return;
761
                                }
762
                                // Assign new ID and update display
763
                                if (csi != null) {
764
                                        csi.sendIDAssignment(curID, newID);
765
                                }
766
                                for (int i = 0; i < robotIcons.size(); i++) {
767
                                        RobotIcon r = robotIcons.get(i);
768
                                        if (r.id == curID) {
769
                                                r.id = newID;
770
                                                r.color = Color.GREEN;
771
                                                break;
772
                                        }
773
                                }
774
                                lblSelected.setText("" + newID);
775
                        } else if (source == btnF) { // Robot Movement Controls
776
                                vectorController.setMaxForward();
777
                                vectorController.sendToServer();
778
                        } else if (source == btnB) {
779
                                vectorController.setMaxReverse();
780
                                vectorController.sendToServer();
781
                        } else if (source == btnL) {
782
                                vectorController.setMaxLeft();
783
                                vectorController.sendToServer();
784
                        } else if (source == btnR) {
785
                                vectorController.setMaxRight();
786
                                vectorController.sendToServer();
787
                        } else if (source == btnActivate) {
788
                                vectorController.setZero();
789
                                vectorController.sendToServer();
790
                        } else if (source == btnCommand_MoveTo) { // Robot Commands (non-movement)
791
                                if (selectedBot < 0) {
792
                                        return;
793
                                }
794
                                panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
795
                                setWaypoint = true;
796
                                setWaypointID = selectedBot;
797
                        } else if (source == btnCommand_MoveAll) {
798
                                panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
799
                                setWaypoint = true;
800
                                setWaypointID = -1;
801
                        } else if (source == btnCommand_StopTask) {
802

    
803
                        } else if (source == btnCommand_ResumeTask) {
804

    
805
                        } else if (source == btnCommand_ChargeNow) {
806

    
807
                        } else if (source == btnCommand_StopCharging) {
808

    
809
                        } else if (source == btnAddTask) { // Queue Management
810
                                taskAddWindow.prompt();
811
                        } else if (source == btnRemoveTask) {
812
                                if (taskList.getSelectedIndex() >= 0) {
813
                                        csi.sendQueueRemove(taskList.getSelectedIndex());
814
                                }
815
                                csi.sendQueueUpdate();
816
                        } else if (source == btnMoveTaskUp) {
817
                                csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
818
                                csi.sendQueueUpdate();
819
                        } else if (source == btnMoveTaskDown) {
820
                                csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
821
                                csi.sendQueueUpdate();
822
                        } else if (source == btnUpdateTasks) {
823
                                csi.sendQueueUpdate();
824
                        }
825

    
826
                        repaint();
827
                }
828
        }
829

    
830
        /*
831
        * DataUpdater thread.
832
        *                The purpose of this thread is to request data from the server at regular intervals.
833
        *
834
        */
835
        class DataUpdater extends Thread {
836
                final int DATAUPDATER_DELAY = 250;
837

    
838
                public DataUpdater () {
839
                        super("Colonet DataUpdater");
840
                }
841

    
842
                public void run () {
843
                        String line;
844
                        while (true) {
845
                                try {
846
                                        //request more data
847
                                        if (csi != null && csi.isReady()) {
848
                                                csi.sendPositionRequest();
849
                                                csi.sendXBeeIDRequest();
850
                                                if (cmbRobotNum.getSelectedIndex() > 0) {
851
                                                        String sel = (String) cmbRobotNum.getSelectedItem();
852
                                                        int num = Integer.parseInt(sel);
853
                                                        csi.sendBatteryRequest(num);
854
                                                }
855
                                        }
856
                                        Thread.sleep(DATAUPDATER_DELAY);
857
                                } catch (InterruptedException e) {
858
                                        return;
859
                                }
860
                        }
861
                }
862
        }
863

    
864
        /*
865
        * GraphicsPanel class
866
        * An extension of JPanel, designed for holding an image that will be repainted regularly.
867
        */
868
        class GraphicsPanel extends JPanel {
869
                protected Image img;
870

    
871
                public GraphicsPanel (Image img) {
872
                        this(img, true);
873
                }
874

    
875
                public GraphicsPanel (Image img, boolean isDoubleBuffered) {
876
                        super(isDoubleBuffered);
877
                        this.img = img;
878
                }
879

    
880
                public void paint (Graphics g) {
881
                        // Place the buffered image on the screen, inside the panel
882
                        g.drawImage(img, 0, 0, Color.WHITE, this);
883
                }
884
        }
885

    
886
        /*
887
        * WebcamPanel class
888
        * Enables more efficient image handling in a component-controlled environment
889
        */
890
        class WebcamPanel extends JPanel {
891
                int BORDER = 16;        // this is arbitrary. it makes the image look nice inside a border.
892
                int BOT_RADIUS = 40;
893
                volatile BufferedImage img;
894
                BufferedImage buffer;
895

    
896
                public WebcamPanel () {
897
                        super(true);
898
                }
899

    
900
                public synchronized void setImage (BufferedImage newimg) {
901
                        if (img != null) {
902
                                img.flush();
903
                        }
904
                        System.gc();
905
                        img = newimg;
906
                        repaint();
907
                }
908

    
909
                public synchronized void paint (Graphics g) {
910
                        if (img == null) {
911
                                return;
912
                        }
913

    
914
                        // Calculate scaling
915
                        int maxWidth = getWidth() - 2*BORDER;
916
                        int maxHeight = getHeight() - 2*BORDER;
917
                        double widthRatio = 1.0 * maxWidth / img.getWidth();
918
                        double heightRatio = 1.0 * maxHeight / img.getHeight();
919
                        double scale = 0;
920
                        int newWidth = 0;
921
                        int newHeight = 0;
922
                        int x = 0;
923
                        int y = 0;
924

    
925
                        if (widthRatio > heightRatio) {         //height is the limiting factor
926
                                scale = heightRatio;
927
                                newHeight = maxHeight;
928
                                newWidth = (int) (img.getWidth() * scale);
929
                                y = BORDER;
930
                                x = (maxWidth - newWidth) / 2 + BORDER;
931
                        } else {        //width is the limiting factor
932
                                scale = widthRatio;
933
                                newWidth = maxWidth;
934
                                newHeight = (int) (img.getHeight() * scale);
935
                                x = BORDER;
936
                                y = (maxHeight - newHeight) / 2 + BORDER;
937
                        }
938

    
939
                        // Draw everything onto the buffer
940
                        buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
941
                        Graphics2D bufferedGraphics = (Graphics2D)buffer.getGraphics();
942
                        bufferedGraphics.setColor(Color.GRAY);
943
                        bufferedGraphics.fillRect(0, 0, this.getWidth(), this.getHeight());
944
                        Image imgScaled = img.getScaledInstance(newWidth, newHeight, Image.SCALE_FAST);
945
                        bufferedGraphics.drawImage(imgScaled, x, y, this);
946

    
947

    
948
                        // Draw Identifiers and battery levels
949
                        if (robotIcons != null) {
950
                                bufferedGraphics.setStroke(new BasicStroke(2));
951
                                for (int i = 0; i < robotIcons.size(); i++) {
952
                                        RobotIcon r = robotIcons.get(i);
953
                                        bufferedGraphics.setColor(r.color);
954
                                        // Identifier circle
955
                                        int px = (int) (x + r.x * scale);
956
                                        int py = (int) (y + r.y * scale);
957
                                        bufferedGraphics.drawOval(px-RADIUS, py-RADIUS, 2*r.RADIUS, 2*r.RADIUS);
958
                                        // Battery
959
                                        //if (r.battery >= 0) {
960
                                                bufferedGraphics.setColor(Color.GREEN);
961
                                                bufferedGraphics.fillRect(px+20, py+20, 30, 10);
962
                                                bufferedGraphics.setColor(Color.BLACK);
963
                                                bufferedGraphics.drawRect(px+20, py+20, 50, 10);
964
                                        //}
965
                                        // If the robot has a destination, draw the vector
966
                                        if (r.destx >= 0) {
967
                                                bufferedGraphics.drawLine(px, py, (int)(x + r.destx * scale), (int)(y + r.desty * scale));
968
                                        }
969
                                }
970
                        }
971

    
972
                        // Identify currently-selected robot
973
                        for (int i = 0; i < robotIcons.size(); i++) {
974
                                RobotIcon r = robotIcons.get(i);
975
                                if (r.id == selectedBot) {
976
                                        int px = (int) (x + r.x * scale);
977
                                        int py = (int) (y + r.y * scale);
978
                                        bufferedGraphics.setColor(Color.BLACK);
979
                                        bufferedGraphics.drawOval(px-RADIUS-6, py-RADIUS-6, 2*r.RADIUS+12, 2*r.RADIUS+12);
980
                                }
981
                        }
982

    
983
                        //Display buffered content
984
                        g.drawImage(buffer, 0, 0, this);
985
                }
986

    
987
                /*
988
                * Convert a click on the webcam panel to a coordinate that is consistent with the
989
                * original size of the image that the panel contains.
990
                */
991
                public Point convertClick (MouseEvent e) {
992
                        if (img == null) {
993
                                return new Point(e.getX(), e.getY());
994
                        }
995

    
996
                        // Calculate scaling
997
                        int clickx = e.getX();
998
                        int clicky = e.getY();
999
                        int maxWidth = getWidth() - 2*BORDER;
1000
                        int maxHeight = getHeight() - 2*BORDER;
1001
                        double widthRatio = 1.0 * maxWidth / img.getWidth();
1002
                        double heightRatio = 1.0 * maxHeight / img.getHeight();
1003
                        double scale = 0;
1004
                        int newWidth = 0;
1005
                        int newHeight = 0;
1006
                        int px = 0;
1007
                        int py = 0;
1008

    
1009
                        if (widthRatio > heightRatio) {         //height is the limiting factor
1010
                                scale = heightRatio;
1011
                                newHeight = maxHeight;
1012
                                newWidth = (int) (img.getWidth() * scale);
1013
                                py = clicky - BORDER;
1014
                                px = clickx - BORDER - (maxWidth - newWidth) / 2;
1015
                        } else {        //width is the limiting factor
1016
                                scale = widthRatio;
1017
                                newWidth = maxWidth;
1018
                                newHeight = (int) (img.getHeight() * scale);
1019
                                px = clickx - BORDER;
1020
                                py = clicky - BORDER - (maxHeight - newHeight) / 2;
1021
                        }
1022
                        py = (int) (py / scale);
1023
                        px = (int) (px / scale);
1024

    
1025
                        txtMatrix.setText("(" + clickx + "," + clicky + ") => (" + px + "," + py + ")");
1026
                        return new Point(px, py);
1027
                }
1028
        }
1029

    
1030
        /*
1031
        * WebcamLoader class
1032
        * Handles the loading of the webcam image.
1033
        */
1034
        class WebcamLoader extends Thread
1035
        {
1036
                final int WEBCAMLOADER_DELAY = 400;
1037
                final String IMAGE_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet.jpg";
1038

    
1039
                URL imagePath;
1040

    
1041
                MediaTracker mt;
1042
                BufferedImage image;
1043
                Random rand;
1044

    
1045
                public WebcamLoader (JApplet applet)
1046
                {
1047
                        super("ColonetWebcamLoader");
1048
                        mt = new MediaTracker(applet);
1049
                        ImageIO.setUseCache(false);
1050
                        rand = new Random();
1051
                }
1052

    
1053
                public void run ()
1054
                {
1055
                        while (true) {
1056
                                try {
1057
                                        Thread.sleep(WEBCAMLOADER_DELAY);
1058
                                        if (image != null)
1059
                                                image.flush();
1060
                                        System.gc();
1061
                                        try {
1062
                                                imagePath = new URL(IMAGE_PATH + "?rand=" + rand.nextInt(50000));
1063
                                        } catch (MalformedURLException e) {
1064
                                                System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n");
1065
                                        }
1066
                                        image = ImageIO.read(imagePath);
1067
                                        // The MediaTracker waitForID pauses the thread until the image is loaded.
1068
                                        // We don't want to display a half-downloaded image.
1069
                                        mt.addImage(image, 1);
1070
                                        mt.waitForID(1);
1071
                                        mt.removeImage(image);
1072
                                        // Save
1073
                                        panelWebcam.setImage(image);
1074
                                } catch (InterruptedException e) {
1075
                                        return;
1076
                                } catch (java.security.AccessControlException e) {
1077
                                        csi.warn("Could not load webcam.\n" + e);
1078
                                        return;
1079
                                } catch (IOException e) {
1080
                                        log.append("IOException while trying to load image.");
1081
                                }
1082
                        }
1083
                }
1084
        }
1085

    
1086
        /*
1087
        * VectorController class
1088
        * Manages robot motion control graphically
1089
        */
1090
        class VectorController extends GraphicsPanel implements MouseListener, MouseMotionListener {
1091
                int x, y, cx, cy;
1092
                int width, height;
1093
                int side;
1094

    
1095
                public VectorController (Image img) {
1096
                        super (img);
1097
                        width = img.getWidth(null);
1098
                        height = img.getHeight(null);
1099
                        cx = img.getWidth(null)/2;
1100
                        cy = img.getHeight(null)/2;
1101
                        x = cx;
1102
                        y = cy;
1103
                        if (width < height) {
1104
                                side = width;
1105
                        } else {
1106
                                side = height;
1107
                        }
1108
                        this.addMouseListener(this);
1109
                        this.addMouseMotionListener(this);
1110
                }
1111

    
1112
                public void setPoint (int x, int y) {
1113
                        if (isValidPoint(x, y)) {
1114
                                this.x = x;
1115
                                this.y = y;
1116
                                repaint();
1117
                        }
1118
                }
1119

    
1120
                public boolean isValidPoint (int x, int y) {
1121
                        double xterm = Math.pow(1.0*(x - cx)/(side/2), 2);
1122
                        double yterm = Math.pow(1.0*(y - cy)/(side/2), 2);
1123
                        return (xterm + yterm <= 1);
1124
                }
1125

    
1126
                public void notifyMouseEvent (MouseEvent e, boolean send) {
1127
                        if (!isValidPoint(e.getX(), e.getY())) {
1128
                                return;
1129
                        }
1130

    
1131
                        vectorController.setPoint(e.getX(), e.getY());
1132
                        vectorController.repaint();
1133
                        if (send) {
1134
                                vectorController.sendToServer();
1135
                        }
1136
                }
1137

    
1138
                public void mouseExited(MouseEvent e) {
1139
                }
1140
                public void mouseEntered(MouseEvent e) {
1141
                }
1142
                public void mouseReleased(MouseEvent e) {
1143
                        this.notifyMouseEvent(e, true);
1144
                }
1145
                public void mouseClicked(MouseEvent e) {
1146
                        this.notifyMouseEvent(e, false);
1147
                }
1148
                public void mousePressed(MouseEvent e) {
1149
                }
1150
                public void mouseDragged(MouseEvent e) {
1151
                        vectorController.notifyMouseEvent(e, false);
1152
                }
1153
                public void mouseMoved(MouseEvent e) {
1154
                }
1155

    
1156
                public int getSpeed () {
1157
                        int dx = x - cx;
1158
                        int dy = y - cy;
1159
                        int v = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) );
1160
                        return v;
1161
                }
1162

    
1163
                /**
1164
                * Returns the angle of the control vector in positive degrees west of north,
1165
                * or negative degrees east of north, whichever is less than or equal to
1166
                * 180 degrees total.
1167
                */
1168
                public int getAngle () {
1169
                        int dx = x - cx;
1170
                        int dy = cy - y;
1171
                        // find reference angle in radians
1172
                        double theta = Math.atan2(Math.abs(dx), Math.abs(dy));
1173
                        // transform to degrees
1174
                        theta = theta * 180 / Math.PI;
1175
                        // adjust for quadrant
1176
                        if (dx < 0 && dy < 0)
1177
                                theta = 90 + theta;
1178
                        else if (dx < 0 && dy >= 0)
1179
                                theta = 90 - theta;
1180
                        else if (dx >= 0 && dy < 0)
1181
                                theta = -90 - theta;
1182
                        else
1183
                                theta = -90 + theta;
1184
                        return (int) theta;
1185
                }
1186

    
1187
                public void paint (Graphics g) {
1188
                        g.setColor(Color.BLACK);
1189
                        g.fillRect(0, 0, width, height);
1190
                        ((Graphics2D)g).setStroke(new BasicStroke(1));
1191
                        g.setColor(Color.RED);
1192
                        g.drawOval(cx-side/2, cy-side/2, side, side);
1193
                        ((Graphics2D)g).setStroke(new BasicStroke(2));
1194
                        g.setColor(Color.GREEN);
1195
                        g.drawLine(cx, cy, x, y);
1196
                        g.fillOval(x-3, y-3, 6, 6);
1197
                }
1198

    
1199
                public void setMaxForward () {
1200
                        setPoint(cx, cy - (side/2) + 1);
1201
                }
1202

    
1203
                public void setMaxReverse () {
1204
                        setPoint(cx, cy + (side/2) - 1);
1205
                }
1206

    
1207
                public void setMaxLeft () {
1208
                        setPoint(cx - (side/2) + 1, cy);
1209
                }
1210

    
1211
                public void setMaxRight () {
1212
                        setPoint(cx + (side/2) - 1, cy);
1213
                }
1214

    
1215
                public void setZero () {
1216
                        setPoint(cx, cy);
1217
                }
1218

    
1219
                public void sendToServer () {
1220
                        System.out.println("Attempting to send angle = " + getAngle() + ", speed = " + getSpeed() + "");
1221
                        String dest = ColonetServerInterface.GLOBAL_DEST;
1222
                        if (cmbRobotNum != null && cmbRobotNum.getSelectedIndex() > 0) {
1223
                                dest = (String)cmbRobotNum.getSelectedItem();
1224
                        }
1225

    
1226
                        if (csi != null) {
1227
                                /*
1228
                                csi.sendData(ColonetServerInterface.MOVE + " " + getSpeed() + " " + getAngle(), dest);
1229
                                */
1230

    
1231
                                //Directional commands
1232
                                if (x > cx && y == cy) {        //move right
1233
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", dest);
1234
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", dest);
1235
                                } else if (x < cx && y == cy) {         //move left
1236
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", dest);
1237
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", dest);
1238
                                } else if (x == cx && y > cy) {         //move forward
1239
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 225", dest);
1240
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 225", dest);
1241
                                } else if (x == cx && y < cy) {         //move backward
1242
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 225", dest);
1243
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 225", dest);
1244
                                } else if (x == cx && y == cy) {        //stop!
1245
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 0", dest);
1246
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 0", dest);
1247
                                }
1248
                        }
1249
                }
1250

    
1251
        }
1252

    
1253
        /*
1254
        * TaskAddWindow class
1255
        * A window that provides a simple way to add tasks to a task queue.
1256
        */
1257
        class TaskAddWindow extends JFrame implements ActionListener, ListSelectionListener {
1258
                JPanel panelButtons;
1259
                JPanel panelParameters;
1260
                JPanel panelSouth;
1261
                JPanel panelSelection;
1262
                JButton btnSubmit;
1263
                JButton btnCancel;
1264
                DefaultListModel availableListModel;
1265
                JList availableList;
1266
                JScrollPane spAvailableTasks;
1267
                JTextArea txtDescription;
1268
                JTextField txtParameters;
1269

    
1270
                public TaskAddWindow () {
1271
                        super("Add a Task");
1272
                        super.setSize(500,500);
1273
                        super.setLayout(new BorderLayout());
1274

    
1275
                        // set up buttons
1276
                        btnSubmit = new JButton("Submit");
1277
                        btnCancel = new JButton("Cancel");
1278
                        panelButtons = new JPanel();
1279
                        panelButtons.setLayout(new FlowLayout());
1280
                        panelButtons.add(btnSubmit);
1281
                        panelButtons.add(btnCancel);
1282
                        this.getRootPane().setDefaultButton(btnSubmit);
1283

    
1284
                        // set up task list
1285
                        availableListModel = new DefaultListModel();
1286
                        availableListModel.addElement("Map the Environment");
1287
                        availableListModel.addElement("Clean Up Chemical Spill");
1288
                        availableListModel.addElement("Grow Plants");
1289
                        availableListModel.addElement("Save the Cheerleader");
1290
                        availableListModel.addElement("Save the World");
1291
                        availableList = new JList(availableListModel);
1292
                        availableList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1293
                        availableList.setSelectedIndex(-1);
1294
                        spAvailableTasks = new JScrollPane(availableList);
1295
                        spAvailableTasks.setBorder(BorderFactory.createTitledBorder("Select A Task"));
1296
                        txtDescription = new JTextArea();
1297
                        txtDescription.setEditable(false);
1298
                        txtDescription.setLineWrap(true);
1299
                        txtDescription.setWrapStyleWord(true);
1300
                        txtDescription.setBorder(BorderFactory.createTitledBorder("Description"));
1301

    
1302
                        //set up parameter area
1303
                        panelParameters = new JPanel();
1304
                        panelParameters.setLayout(new BorderLayout());
1305
                        txtParameters = new JTextField();
1306
                        panelParameters.add(new JLabel("Optional parameters for this task: "), BorderLayout.WEST);
1307
                        panelParameters.add(txtParameters);
1308

    
1309
                        // assemble objects
1310
                        panelSelection = new JPanel();
1311
                        panelSelection.setLayout(new GridLayout(1,2));
1312
                        panelSelection.add(spAvailableTasks);
1313
                        panelSelection.add(txtDescription);
1314

    
1315
                        panelSouth = new JPanel();
1316
                        panelSouth.setLayout(new GridLayout(2,1));
1317
                        panelSouth.add(panelParameters);
1318
                        panelSouth.add(panelButtons);
1319

    
1320
                        this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
1321
                        this.getContentPane().add(panelSelection, BorderLayout.CENTER);
1322
                        this.setLocationRelativeTo(null);
1323

    
1324
                        // add listeners here
1325
                        availableList.addListSelectionListener(this);
1326
                        btnSubmit.addActionListener(this);
1327
                        btnCancel.addActionListener(this);
1328
                }
1329

    
1330
                public void prompt () {
1331
                        this.setVisible(true);
1332
                }
1333

    
1334
                private String getDescription (int index) {
1335
                        if (index < 0)
1336
                                return "";
1337
                        switch (index) {
1338
                                case 0: return "SLAM and junk";
1339
                                case 1: return "I'm not sure this works";
1340
                                case 2: return "Push them into the light";
1341
                                case 3: return "...";
1342
                                case 4: return "...";
1343

    
1344
                                default: return "Task not recognized";
1345
                        }
1346
                }
1347

    
1348
                public void actionPerformed (ActionEvent e) {
1349
                        Object source = e.getSource();
1350
                        if (source == btnSubmit) {
1351
                                txtParameters.setText(txtParameters.getText().trim());
1352

    
1353

    
1354
                                this.setVisible(false);
1355
                        } else if (source == btnCancel) {
1356
                                this.setVisible(false);
1357
                        }
1358
                }
1359

    
1360
                public void valueChanged (ListSelectionEvent e) {
1361
                        int index = availableList.getSelectedIndex();
1362
                        if (index >= 0)
1363
                                txtDescription.setText(getDescription(index));
1364
                }
1365

    
1366
        }
1367

    
1368
        /*
1369
        *         BatteryIcon class
1370
        *         Graphical representation of battery level
1371
        */
1372
        class BatteryIcon implements Icon {
1373
                private int width;
1374
                private int height;
1375
                private int level;
1376

    
1377
                /**
1378
                * Constructs a new BatteryIcon with all default parameters.
1379
                * Default width and height are 50.
1380
                * Default level is 100.
1381
                */
1382
                public BatteryIcon(){
1383
                        this(100, 50, 50);
1384
                }
1385

    
1386
                /**
1387
                * Constructs a new BatteryIcon with default width and height, and with the specified level.
1388
                * Default width and height are 50.
1389
                */
1390
                public BatteryIcon(int startLevel){
1391
                        this(startLevel, 50, 50);
1392
                }
1393

    
1394
                /**
1395
                * Constructs a new BatteryIcon with the specified level, width, and height.
1396
                */
1397
                public BatteryIcon(int startLevel, int w, int h){
1398
                        level = startLevel;
1399
                        width = w;
1400
                        height = h;
1401
                }
1402

    
1403
                public void paintIcon(Component c, Graphics g, int x, int y) {
1404
                        Graphics2D g2d = (Graphics2D) g.create();
1405
                        //clear the background
1406
                        g2d.setColor(Color.WHITE);
1407
                        g2d.fillRect(x + 1, y + 1, width - 2, height - 2);
1408
                        //outline
1409
                        g2d.setColor(Color.BLACK);
1410
                        g2d.drawRect((int)(x + width*.3), y + 2, (int)(width*.4), height - 4);
1411
                        //battery life rectangle
1412

    
1413
                        if (level > 50)
1414
                                g2d.setColor(Color.GREEN);
1415
                        else if (level > 25)
1416
                                g2d.setColor(Color.YELLOW);
1417
                        else
1418
                                g2d.setColor(Color.RED);
1419

    
1420
                        int greenX = (int)(x + 1 + width*.3);
1421
                        int greenY = (int)((y+3) + Math.abs(level-100.0)*(height-6)/(100));
1422
                        int greenWidth = (int)(width*.4 - 2)+1;
1423
                        int greenHeight = 1+(int)(level-0.0)*(height-6)/(100);
1424
                        g2d.fillRect(greenX, greenY, greenWidth, greenHeight);
1425
                        //text
1426
                        g2d.setColor(Color.BLACK);
1427
                        g2d.drawString(level + "%", greenX + greenWidth/2 - 10, greenY + greenHeight/2 + 5);
1428

    
1429
                        g2d.dispose();
1430
                }
1431

    
1432
                /**
1433
                * Sets the battery level for this BatteryIcon. The level should be given in raw form, i.e. 0-255 directly
1434
                * from the robot. The value will be converted to a representative percentage automatically.
1435
                *
1436
                * @param newLevel the new battery reading from the robot that this BatteryIcon will display.
1437
                */
1438
                public void setLevel(int newLevel) {
1439
                        level = convert(newLevel);
1440
                        repaint();
1441
                        System.out.println("Updated level to " + level);
1442
                }
1443

    
1444
                public int getIconWidth() {
1445
                        return width;
1446
                }
1447

    
1448
                public int getIconHeight() {
1449
                        return height;
1450
                }
1451

    
1452
                /**
1453
                * Converts a robot battery reading into representable form.
1454
                * Readings from the robot are returned as raw values, 0-255. This method converts the reading into a value
1455
                * from 0 to 100 so that the practical remaining charge is represented.
1456
                *
1457
                * @param level The battery level as returned by the robot.
1458
                * @returns The representable battery percentage.
1459
                */
1460
                private int convert (int level) {
1461
                        // TODO: make this a forreals conversion.
1462
                        return (int) (100.0 * level / 170);
1463
                }
1464
        }
1465
}