Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (44 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
        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.add(panelWebcam, "Webcam");
157
                //tabPaneMain.add(panelGraph, "Graph");
158

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

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

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

    
195
                // Robot direction panel
196
                panelRobotDirection = new JPanel();
197
                panelRobotDirectionButtons = new JPanel();
198
                btnF = new JButton("^");
199
                btnB = new JButton("v");
200
                btnL = new JButton("<");
201
                btnR = new JButton(">");
202
                btnActivate = new JButton("o");
203
                panelRobotDirectionButtons.setLayout(new GridLayout(1,5));
204
                panelRobotDirectionButtons.add(btnActivate);
205
                panelRobotDirectionButtons.add(btnF);
206
                panelRobotDirectionButtons.add(btnB);
207
                panelRobotDirectionButtons.add(btnL);
208
                panelRobotDirectionButtons.add(btnR);
209

    
210
                imageVectorControl = gc.createCompatibleImage(VECTOR_CONTROLLER_WIDTH, VECTOR_CONTROLLER_HEIGHT);
211
                vectorController = new VectorController(imageVectorControl);
212
                panelRobotDirection.setLayout(new BorderLayout());
213
                panelRobotDirection.add(vectorController, BorderLayout.CENTER);
214
                panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
215

    
216
                // Robot Control and Commands
217
                panelRobotCommands = new JPanel();
218
                panelRobotCommands.setLayout(new GridLayout(5,2));
219
                cmbRobotNum = new JComboBox();
220
                // Battery subset
221
                batteryIcon = new BatteryIcon(0);
222
                lblBattery = new JLabel(batteryIcon);
223
                lblSelected = new JLabel("None");
224
                // Command subset
225
                setWaypoint = false;
226
                setWaypointID = -1;
227
                btnAssignID = new JButton("Assign ID");
228
                btnCommand_MoveTo = new JButton("Move to ...");
229
                btnCommand_MoveAll = new JButton("Move all ...");
230
                btnCommand_StopTask = new JButton("Stop Current Task");
231
                btnCommand_ResumeTask = new JButton("Resume Current Task");
232
                btnCommand_ChargeNow = new JButton("Recharge Now");
233
                btnCommand_StopCharging = new JButton("Stop Recharging");
234
                panelRobotCommands.add(new JLabel("Select Robot to Control: "));
235
                panelRobotCommands.add(cmbRobotNum);
236
                panelRobotCommands.add(new JLabel("Battery Level: "));
237
                panelRobotCommands.add(lblBattery);
238
                panelRobotCommands.add(new JLabel("Selected Icon: "));
239
                panelRobotCommands.add(lblSelected);
240
                panelRobotCommands.add(btnAssignID);
241
                panelRobotCommands.add(new JLabel(""));
242
                panelRobotCommands.add(btnCommand_MoveTo);
243
                panelRobotCommands.add(btnCommand_MoveAll);
244
                //panelRobotCommands.add(btnCommand_StopTask);
245
                //panelRobotCommands.add(btnCommand_ResumeTask);
246
                //panelRobotCommands.add(btnCommand_ChargeNow);
247
                //panelRobotCommands.add(btnCommand_StopCharging);
248
                panelRobotControl = new JPanel();
249
                panelRobotControl.setLayout(new GridLayout(2,1));
250
                panelRobotControl.add(panelRobotDirection);
251
                panelRobotControl.add(panelRobotCommands);
252

    
253

    
254
                // Task Manager
255
                panelTaskManager = new JPanel();
256
                panelTaskManager.setLayout(new BorderLayout());
257
                taskListModel = new DefaultListModel();
258
                taskList = new JList(taskListModel);
259
                taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
260
                taskList.setSelectedIndex(0);
261
                spTaskManager = new JScrollPane(taskList);
262
                panelTaskManagerControls = new JPanel();
263
                panelTaskManagerControls.setLayout(new GridLayout(1,4));
264
                panelTaskManagerControlsPriority = new JPanel();
265
                panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
266
                btnAddTask = new JButton("Add...");
267
                btnRemoveTask = new JButton("Remove");
268
                btnMoveTaskUp = new JButton("^");
269
                btnMoveTaskDown = new JButton("v");
270
                btnUpdateTasks = new JButton("Update");
271
                panelTaskManagerControlsPriority.add(btnMoveTaskUp);
272
                panelTaskManagerControlsPriority.add(btnMoveTaskDown);
273
                panelTaskManagerControls.add(btnAddTask);
274
                panelTaskManagerControls.add(btnRemoveTask);
275
                panelTaskManagerControls.add(btnUpdateTasks);
276
                panelTaskManagerControls.add(panelTaskManagerControlsPriority);
277
                panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
278
                panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
279
                panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
280
                taskAddWindow = new TaskAddWindow();
281

    
282
                // Message log
283
                log = new JTextArea();
284
                spLog = new JScrollPane(log, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
285
                        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
286
                spLog.setBorder(BorderFactory.createTitledBorder("Log"));
287
                spLog.setPreferredSize(new Dimension(0, 120));
288
                log.setEditable(false);
289

    
290
                // Main control mechanism
291
                panelControl = new JPanel();
292
                panelControl.setLayout(new GridLayout(1,1));
293
                tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
294
                tabPaneControl.setPreferredSize(new Dimension(VECTOR_CONTROLLER_WIDTH, 0));
295
                tabPaneControl.addTab("Connection", panelServerInterface);
296
                tabPaneControl.addTab("Robots", panelRobotControl);
297
                //tabPaneControl.addTab("Tasks", panelTaskManager);
298
                panelControl.add(tabPaneControl);
299

    
300
                // Set up elements in the south
301
                panelSouth = new JPanel();
302
                panelSouth.setLayout(new GridLayout(1,2));
303
                //panelSouth.add(spLog);
304

    
305
                // Put all elements in the ContentPane
306
                this.getContentPane().setLayout(new BorderLayout());
307
                this.getContentPane().add(tabPaneMain, BorderLayout.CENTER);
308
                this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
309
                this.getContentPane().add(panelControl, BorderLayout.EAST);
310
                this.setVisible(true);
311

    
312
                /* Add all listeners here */
313
                // Task Management
314
                btnAddTask.addActionListener(this);
315
                btnRemoveTask.addActionListener(this);
316
                btnMoveTaskUp.addActionListener(this);
317
                btnMoveTaskDown.addActionListener(this);
318
                btnUpdateTasks.addActionListener(this);
319
                // Robot Control
320
                btnF.addActionListener(this);
321
                btnB.addActionListener(this);
322
                btnL.addActionListener(this);
323
                btnR.addActionListener(this);
324
                btnF.addKeyListener(this);
325
                btnB.addKeyListener(this);
326
                btnL.addKeyListener(this);
327
                btnR.addKeyListener(this);
328
                btnActivate.addActionListener(this);
329
                btnActivate.addKeyListener(this);
330
                cmbRobotNum.addKeyListener(this);
331
                btnCommand_MoveTo.addActionListener(this);
332
                btnCommand_MoveAll.addActionListener(this);
333
                btnCommand_StopTask.addActionListener(this);
334
                btnCommand_ResumeTask.addActionListener(this);
335
                btnCommand_ChargeNow.addActionListener(this);
336
                btnCommand_StopCharging.addActionListener(this);
337
                // Other
338
                btnConnect.addActionListener(this);
339
                btnGetXBeeIDs.addActionListener(this);
340
                btnAssignID.addActionListener(this);
341
                panelWebcam.addMouseListener(this);
342
        }
343

    
344
        public void run () {
345
                while (true) {
346
                        repaint();
347
                        try {
348
                                Thread.sleep(90);
349
                        } catch (InterruptedException e) {
350
                                return;
351
                        }
352
                }
353
        }
354

    
355
        public void paint (Graphics g) {
356
                super.paint(g);
357
        }
358

    
359
        public void update (Graphics g) {
360
                paint(g);
361
        }
362

    
363
        /**
364
        * Gets the JTextArea used for storing the activity log. This method returns a reference to the
365
        * JTextArea that stores the log. The log can contain any activity that is revelant to the use
366
        * of the applet, and may optionally display debugging information.
367
        *
368
        * @return the JTextArea where BOM matrix information is stored.
369
        */
370
        public JTextArea getLog () {
371
                return log;
372
        }
373

    
374
        /**
375
        * Gets the JTextArea used for storing the BOM matrix data. This method returns a reference to the
376
        * JTextArea that stores the BOM matrix. The values in the matrix are stored as integers separated
377
        * by spaces, and the lines should be separated by a newline.
378
        *
379
        * @return the JTextArea where BOM matrix information is stored.
380
        */
381
        public JTextArea getMatrixInput () {
382
                return txtMatrix;
383
        }
384

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

    
414
                                if (j < num - 1) {
415
                                        txtMatrix.append(" ");
416
                                }
417
                        }
418

    
419
                        if (i < num - 1) {
420
                                txtMatrix.append("\n");
421
                        }
422
                }
423
                repaint();
424
        }
425

    
426
        public void connect () {
427
                lblConnectionStatus.setText("Status: Connecting...");
428
                webcamLoader = new WebcamLoader(this);
429
                dataUpdater = new DataUpdater();
430
                csi = new ColonetServerInterface(this);
431
                csi.connect(txtHost.getText(), txtPort.getText());
432
                if (!csi.isReady()) {
433
                        lblConnectionStatus.setText("Status: Offline");
434
                } else {
435
                        btnConnect.setText("Disconnect");
436
                        lblConnectionStatus.setText("Status: Connected");
437
                        dataUpdater.start();
438
                        webcamLoader.start();
439
                }
440
        }
441

    
442
        public void disconnect () {
443
                lblConnectionStatus.setText("Status: Disconnecting...");
444
                dataUpdater.interrupt();
445
                csi.disconnect();
446
                csi = null;
447
                btnConnect.setText("Connect");
448
                lblConnectionStatus.setText("Status: Disconnected");
449
        }
450

    
451
        /**
452
        * Parses a String containing a task queue update.
453
        * Format is currently not specified.
454
        * This method currently does nothing.
455
        *
456
        * @param line the String containing task queue update information.
457
        */
458
        public void parseQueue (String line) {
459
                log.append("Got queue update\n");
460
                //TODO: display new queue data in tasks tab
461
        }
462

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

    
488
                //update the list of robots to control
489
                //but save the old value first
490
                Object oldSelection = cmbRobotNum.getSelectedItem();
491
                cmbRobotNum.removeAllItems();
492
                cmbRobotNum.addItem(new String("         All         "));
493
                for (int i = 0; i < num; i++) {
494
                        cmbRobotNum.addItem(new String("" + xbeeID[i]));
495
                }
496
                cmbRobotNum.setSelectedItem(oldSelection);
497
                repaint();
498
        }
499

    
500
        /**
501
        * Parses a String containing battery information.
502
        * The ColonetServerInterface receives Strings of battery information.         (For encoding
503
        * information, see the ColonetServerInterface documentation.)         This method takes
504
        * a string of the form "[command code] [command code] [robot ID] [value]"
505
        * with tokens separated by spaces and containing no brackets.
506
        * The [command code]s are predefined values identifying this String as a battery
507
        * information String, [robot ID] is an integer, and [value] is a battery measurement.
508
        * This updates the batery information for a single robot.
509
        *
510
        *
511
        * @param line the String containing battery information.
512
        * @see ColonetServerInterface#sendBatteryRequest(int)
513
        */
514
        public void parseBattery (String line) {
515
                String [] str = line.split(" ");
516
                int botNum = Integer.parseInt(str[2]);
517
                int level = Integer.parseInt(str[3]);
518
                int selected = -1;
519
                try {
520
                        selected = Integer.parseInt((String)cmbRobotNum.getSelectedItem());
521
                } catch (Exception e) {
522
                        System.out.println("Exception in parseBattery.");
523
                }
524

    
525
                if (selected == botNum) {
526
                        batteryIcon.setLevel(level);
527
                }
528
                repaint();
529
        }
530

    
531
        /**
532
        * Parses a String containing visual robot position information along with
533
        * canonical ID assignments.
534
        */
535
        public void parsePositions (String line) {
536
                String [] str = line.split(" ");
537
                java.util.List <RobotIcon> newList = new ArrayList <RobotIcon> ();
538

    
539
                for (int i = 2; i < str.length; i+=3) {
540
                        int id = Integer.parseInt(str[i]);
541
                        int x = Integer.parseInt(str[i+1]);
542
                        int y = Integer.parseInt(str[i+2]);
543
                        RobotIcon newIcon = new RobotIcon(id, x, y);
544
                        if (newIcon.id >= 0) {
545
                                newIcon.color = Color.GREEN;
546
                        }
547
                        newList.add(newIcon);
548
                }
549
                robotIcons = newList;
550
                repaint();
551
        }
552

    
553
        //
554
        // MouseListener methods
555
        //
556
        public void mousePressed(MouseEvent e) {
557
                //Start a new Thread to handle the MouseEvent
558
                (new MouseHandler(e)).start();
559
        }
560
        public void mouseExited(MouseEvent e) {
561
        }
562
        public void mouseEntered(MouseEvent e) {
563
        }
564
        public void mouseReleased(MouseEvent e) {
565
        }
566
        public void mouseClicked(MouseEvent e) {
567
        }
568
        public void mouseDragged(MouseEvent e) {
569
        }
570
        public void mouseMoved(MouseEvent e) {
571
        }
572

    
573
        //
574
        // KeyListener methods
575
        //
576
        public void keyPressed (KeyEvent e) {
577
                //Start a new Thread to handle the KeyEvent
578
                (new KeyHandler(e)).start();
579
        }
580
        public void keyReleased (KeyEvent e) {
581
        }
582
        public void keyTyped (KeyEvent e) {
583
        }
584

    
585
        //
586
        // ActionListener method
587
        //
588
        public void actionPerformed (ActionEvent e) {
589
                // Start a new Thread to handle the ActionEvent
590
                (new ActionHandler(e)).start();
591
        }
592

    
593
        class MouseHandler extends Thread {
594
                MouseEvent e;
595

    
596
                public MouseHandler (MouseEvent event) {
597
                        super("MouseHandler");
598
                        this.e = event;
599
                }
600

    
601
                public void run () {
602
                        Point pt = panelWebcam.convertClick(e);
603

    
604
                        // If we are selecting a waypoint (destination) for a specific bot
605
                        if (setWaypoint && setWaypointID        >= 0) {
606
                                setWaypoint = false;
607
                                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
608
                                if (selectedBot < 0) {
609
                                        return;
610
                                }
611

    
612
                                RobotIcon r = robotIcons.get(selectedBot);
613
                                r.destx = pt.x;
614
                                r.desty = pt.y;
615

    
616
                                if (csi != null) {
617
                                        csi.sendAbsoluteMove(r.id, r.destx, r.desty);
618

    
619
                                }
620
                                
621
                                return;
622
                        }
623

    
624
                        // Right-click also means we are moving a robot
625
                        if (e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3) {
626
                                if (selectedBot < 0) {
627
                                        return;
628
                                }
629

    
630
                                RobotIcon r = robotIcons.get(selectedBot);
631
                                r.destx = pt.x;
632
                                r.desty = pt.y;
633

    
634
                                if (csi != null) {
635
                                        csi.sendAbsoluteMove(r.id, r.destx, r.desty);
636
                                }
637

    
638
                                return;
639
                        }
640

    
641
                        // If we are setting all waypoints
642
                        if (setWaypoint) {
643
                                setWaypoint = false;
644
                                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
645
                                for (int i = 0; i < robotIcons.size(); i++) {
646
                                        RobotIcon r = robotIcons.get(i);
647
                                        r.destx = pt.x;
648
                                        r.desty = pt.y;
649
                                }
650
                                
651
                                return;
652
                        }
653

    
654
                        // Otherwise, we are selecting a bot, or doing nothing
655
                        for (int i = 0; i < robotIcons.size(); i++) {
656
                                RobotIcon r = robotIcons.get(i);
657
                                if (r.contains(pt.x, pt.y)) {
658
                                        selectedBot = i;
659
                                        lblSelected.setText(" " + r.id);
660
                                        // Try to select the clicked bot, if its XBee ID is detected.
661
                                        for (int j = 1; j < cmbRobotNum.getItemCount(); j++) {
662
                                                if (Integer.parseInt(cmbRobotNum.getItemAt(j).toString()) == robotIcons.get(selectedBot).id) {
663
                                                        cmbRobotNum.setSelectedIndex(j);
664
                                                }
665
                                        }
666
                                        return;
667
                                }
668
                        }
669

    
670
                        repaint();
671
                }
672
        }
673

    
674
        class KeyHandler extends Thread {
675
                KeyEvent e;
676

    
677
                public KeyHandler (KeyEvent event) {
678
                        super("KeyHandler");
679
                        this.e = event;
680
                }
681

    
682
                public void run () {
683
                        int code = e.getKeyCode();
684
                        if (code == KeyEvent.VK_UP) {
685
                                vectorController.setMaxForward();
686
                                vectorController.sendToServer();
687
                        } else if (code == KeyEvent.VK_DOWN) {
688
                                vectorController.setMaxReverse();
689
                                vectorController.sendToServer();
690
                        } else if (code == KeyEvent.VK_LEFT) {
691
                                vectorController.setMaxLeft();
692
                                vectorController.sendToServer();
693
                        } else if (code == KeyEvent.VK_RIGHT) {
694
                                vectorController.setMaxRight();
695
                                vectorController.sendToServer();
696
                        } else if (code == KeyEvent.VK_S) {
697
                                vectorController.setZero();
698
                                vectorController.sendToServer();
699
                        }
700
                        repaint();
701
                }
702
        }
703

    
704
        class ActionHandler extends Thread {
705
                ActionEvent e;
706

    
707
                public ActionHandler (ActionEvent event) {
708
                        super("ActionHandler");
709
                        this.e = event;
710
                }
711

    
712
                public void run () {
713
                        Object source = e.getSource();
714

    
715
                        // General Actions
716
                        if (source == btnConnect) {
717
                                if (csi == null) {
718
                                        connect();
719
                                } else {
720
                                        disconnect();
721
                                }
722
                        } else if (source == btnGetXBeeIDs) {
723
                                csi.sendXBeeIDRequest();
724
                        } else if (source == btnAssignID) {
725
                                String message;
726
                                if (selectedBot < 0) {
727
                                        return;
728
                                }
729
                                int curID = robotIcons.get(selectedBot).id;
730

    
731
                                if (curID < 0) {
732
                                        message = "That robot is unidentified. Please specify its ID.";
733
                                } else {
734
                                        message = "That robot has ID " + curID + ". You may reassign it now.";
735
                                }
736
                                String result = JOptionPane.showInputDialog(self, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
737
                                if (result == null) {
738
                                        return;
739
                                }
740
                                int newID = -1;
741
                                try {
742
                                        newID = Integer.parseInt(result);
743
                                } catch (Exception ex) {
744
                                        csi.warn("Invalid ID.");
745
                                        return;
746
                                }
747
                                // Assign new ID and update display
748
                                csi.sendIDAssignment(curID, newID);
749
                                robotIcons.get(selectedBot).id = newID;
750
                                robotIcons.get(selectedBot).color = Color.GREEN;
751
                                lblSelected.setText(" " + newID);
752
                        } else if (source == btnF) { // Robot Movement Controls
753
                                vectorController.setMaxForward();
754
                                vectorController.sendToServer();
755
                        } else if (source == btnB) {
756
                                vectorController.setMaxReverse();
757
                                vectorController.sendToServer();
758
                        } else if (source == btnL) {
759
                                vectorController.setMaxLeft();
760
                                vectorController.sendToServer();
761
                        } else if (source == btnR) {
762
                                vectorController.setMaxRight();
763
                                vectorController.sendToServer();
764
                        } else if (source == btnActivate) {
765
                                vectorController.setZero();
766
                                vectorController.sendToServer();
767
                        } else if (source == btnCommand_MoveTo) { // Robot Commands (non-movement)
768
                                if (selectedBot < 0) {
769
                                        return;
770
                                }
771
                                panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
772
                                setWaypoint = true;
773
                                setWaypointID = selectedBot;
774
                        } else if (source == btnCommand_MoveAll) {
775
                                panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
776
                                setWaypoint = true;
777
                                setWaypointID = -1;
778
                        } else if (source == btnCommand_StopTask) {
779

    
780
                        } else if (source == btnCommand_ResumeTask) {
781

    
782
                        } else if (source == btnCommand_ChargeNow) {
783

    
784
                        } else if (source == btnCommand_StopCharging) {
785

    
786
                        } else if (source == btnAddTask) { // Queue Management
787
                                taskAddWindow.prompt();
788
                        } else if (source == btnRemoveTask) {
789
                                if (taskList.getSelectedIndex() >= 0) {
790
                                        csi.sendQueueRemove(taskList.getSelectedIndex());
791
                                }
792
                                csi.sendQueueUpdate();
793
                        } else if (source == btnMoveTaskUp) {
794
                                csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
795
                                csi.sendQueueUpdate();
796
                        } else if (source == btnMoveTaskDown) {
797
                                csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
798
                                csi.sendQueueUpdate();
799
                        } else if (source == btnUpdateTasks) {
800
                                csi.sendQueueUpdate();
801
                        }
802

    
803
                        repaint();
804
                }
805
        }
806

    
807
        /*
808
        * DataUpdater thread.
809
        *                The purpose of this thread is to request data from the server at regular intervals.
810
        *
811
        */
812
        class DataUpdater extends Thread {
813
                final int DATAUPDATER_DELAY = 400;
814

    
815
                public DataUpdater () {
816
                        super("Colonet DataUpdater");
817
                }
818

    
819
                public void run () {
820
                        String line;
821
                        while (true) {
822
                                try {
823
                                        //request more data
824
                                        if (csi != null && csi.isReady()) {
825
                                                                                                csi.sendPositionRequest();
826
                                                csi.sendXBeeIDRequest();
827
                                                if (cmbRobotNum.getSelectedIndex() > 0) {
828
                                                                String sel = (String) cmbRobotNum.getSelectedItem();
829
                                                                int num = Integer.parseInt(sel);
830
                                                        csi.sendBatteryRequest(num);
831
                                                }
832
                                        }
833
                                        Thread.sleep(DATAUPDATER_DELAY);
834
                                } catch (InterruptedException e) {
835
                                        return;
836
                                }
837
                        }
838
                }
839

    
840
        }
841

    
842
        /*
843
        * GraphicsPanel class
844
        * An extension of JPanel, designed for holding an image that will be repainted regularly.
845
        */
846
        class GraphicsPanel extends JPanel {
847
                protected Image img;
848

    
849
                public GraphicsPanel (Image img) {
850
                        this(img, true);
851
                }
852

    
853
                public GraphicsPanel (Image img, boolean isDoubleBuffered) {
854
                        super(isDoubleBuffered);
855
                        this.img = img;
856
                }
857

    
858
                public void paint (Graphics g) {
859
                        // Place the buffered image on the screen, inside the panel
860
                        g.drawImage(img, 0, 0, Color.WHITE, this);
861
                }
862
        }
863

    
864
        /*
865
        * WebcamPanel class
866
        * Enables more efficient image handling in a component-controlled environment
867
        */
868
        class WebcamPanel extends JPanel {
869
                int BORDER = 16;        // this is arbitrary. it makes the image look nice inside a border.
870
                int BOT_RADIUS = 40;
871
                volatile BufferedImage img;
872
                BufferedImage buffer;
873

    
874
                public WebcamPanel () {
875
                        super(true);
876
                }
877

    
878
                public synchronized void setImage (BufferedImage newimg) {
879
                        if (img != null) {
880
                                img.flush();
881
                        }
882
                        System.gc();
883
                        img = newimg;
884
                        repaint();
885
                }
886

    
887
                public synchronized void paint (Graphics g) {
888
                        if (img == null)
889
                                return;
890

    
891
                        // Calculate scaling
892
                        int maxWidth = getWidth() - 2*BORDER;
893
                        int maxHeight = getHeight() - 2*BORDER;
894
                        double widthRatio = 1.0 * maxWidth / img.getWidth();
895
                        double heightRatio = 1.0 * maxHeight / img.getHeight();
896
                        double scale = 0;
897
                        int newWidth = 0;
898
                        int newHeight = 0;
899
                        int x = 0;
900
                        int y = 0;
901

    
902
                        if (widthRatio > heightRatio) {         //height is the limiting factor
903
                                        scale = heightRatio;
904
                                        newHeight = maxHeight;
905
                                        newWidth = (int) (img.getWidth() * scale);
906
                                        y = BORDER;
907
                                        x = (maxWidth - newWidth) / 2 + BORDER;
908
                        } else {        //width is the limiting factor
909
                                        scale = widthRatio;
910
                                        newWidth = maxWidth;
911
                                        newHeight = (int) (img.getHeight() * scale);
912
                                        x = BORDER;
913
                                        y = (maxHeight - newHeight) / 2 + BORDER;
914
                        }
915

    
916
                        // Draw everything onto the buffer
917
                        buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
918
                        Graphics2D bufferedGraphics = (Graphics2D)buffer.getGraphics();
919
                        bufferedGraphics.setColor(Color.GRAY);
920
                        bufferedGraphics.fillRect(0, 0, this.getWidth(), this.getHeight());
921
                        Image imgScaled = img.getScaledInstance(newWidth, newHeight, Image.SCALE_FAST);
922
                        bufferedGraphics.drawImage(imgScaled, x, y, this);
923

    
924

    
925
                        // Draw Identifiers and battery levels
926
                        if (robotIcons != null) {
927
                                        bufferedGraphics.setStroke(new BasicStroke(2));
928
                                        for (int i = 0; i < robotIcons.size(); i++) {
929
                                                RobotIcon r = robotIcons.get(i);
930
                                                bufferedGraphics.setColor(r.color);
931
                                                // Identifier circle
932
                                                int px = (int) (x + r.x * scale);
933
                                                int py = (int) (y + r.y * scale);
934
                                                bufferedGraphics.drawOval(px-RADIUS, py-RADIUS, 2*r.RADIUS, 2*r.RADIUS);
935
                                                // Battery
936
                                                //if (r.battery >= 0) {
937
                                                                bufferedGraphics.setColor(Color.GREEN);
938
                                                                bufferedGraphics.fillRect(px+20, py+20, 30, 10);
939
                                                                bufferedGraphics.setColor(Color.BLACK);
940
                                                                bufferedGraphics.drawRect(px+20, py+20, 50, 10);
941
                                                //}
942
                                                // If the robot has a destination, draw the vector
943
                                                if (r.destx >= 0) {
944
                                                                bufferedGraphics.drawLine(px, py, (int)(x + r.destx * scale), (int)(y + r.desty * scale));
945
                                                }
946
                                        }
947
                        }
948

    
949
                        // Identify currently-selected robot
950
                        if (selectedBot >= 0) {
951
                                        bufferedGraphics.setColor(Color.BLACK);
952
                                        RobotIcon r = robotIcons.get(selectedBot);
953
                                        int px = (int) (x + r.x * scale);
954
                                        int py = (int) (y + r.y * scale);
955
                                        bufferedGraphics.drawOval(px-RADIUS-6, py-RADIUS-6, 2*r.RADIUS+12, 2*r.RADIUS+12);
956
                        }
957

    
958
                        //Display buffered content
959
                        g.drawImage(buffer, 0, 0, this);
960
                }
961

    
962
                /*
963
                * Convert a click on the webcam panel to a coordinate that is consistent with the
964
                * original size of the image that the panel contains.
965
                */
966
                public Point convertClick (MouseEvent e) {
967
                        // Calculate scaling
968
                        int clickx = e.getX();
969
                                int clicky = e.getY();
970
                        int maxWidth = getWidth() - 2*BORDER;
971
                        int maxHeight = getHeight() - 2*BORDER;
972
                        double widthRatio = 1.0 * maxWidth / img.getWidth();
973
                        double heightRatio = 1.0 * maxHeight / img.getHeight();
974
                        double scale = 0;
975
                        int newWidth = 0;
976
                        int newHeight = 0;
977
                        int px = 0;
978
                        int py = 0;
979

    
980
                        if (widthRatio > heightRatio) {         //height is the limiting factor
981
                                        scale = heightRatio;
982
                                        newHeight = maxHeight;
983
                                        newWidth = (int) (img.getWidth() * scale);
984
                                        py = clicky - BORDER;
985
                                        px = clickx - BORDER - (maxWidth - newWidth) / 2;
986
                                        py *= scale;
987
                                        px *= scale;
988
                        } else {        //width is the limiting factor
989
                                        scale = widthRatio;
990
                                        newWidth = maxWidth;
991
                                        newHeight = (int) (img.getHeight() * scale);
992
                                        px = clickx - BORDER;
993
                                        py = clicky - BORDER - (maxHeight - newHeight) / 2;
994
                                        px *= scale;
995
                                        py *= scale;
996
                        }
997

    
998
                                return new Point(px, py);
999

    
1000
                }
1001

    
1002
        }
1003

    
1004
        /*
1005
        * WebcamLoader class
1006
        * Handles the loading of the webcam image.
1007
        */
1008
        class WebcamLoader extends Thread
1009
        {
1010
                final int WEBCAMLOADER_DELAY = 400;
1011
                final String IMAGE_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet.jpg";
1012

    
1013
                URL imagePath;
1014

    
1015
                MediaTracker mt;
1016
                BufferedImage image;
1017
                Random rand;
1018

    
1019
                public WebcamLoader (JApplet applet)
1020
                {
1021
                        super("ColonetWebcamLoader");
1022
                        mt = new MediaTracker(applet);
1023
                        ImageIO.setUseCache(false);
1024
                        rand = new Random();
1025
                }
1026

    
1027
                public void run ()
1028
                {
1029
                        while (true) {
1030
                                try {
1031
                                        Thread.sleep(WEBCAMLOADER_DELAY);
1032
                                        if (image != null)
1033
                                                image.flush();
1034
                                        System.gc();
1035
                                        try {
1036
                                                                imagePath = new URL(IMAGE_PATH + "?rand=" + rand.nextInt(50000));
1037
                                                        } catch (MalformedURLException e) {
1038
                                                                System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n");
1039
                                                        }
1040
                                        image = ImageIO.read(imagePath);
1041
                                        // The MediaTracker waitForID pauses the thread until the image is loaded.
1042
                                        // We don't want to display a half-downloaded image.
1043
                                        mt.addImage(image, 1);
1044
                                        mt.waitForID(1);
1045
                                        mt.removeImage(image);
1046
                                        // Save
1047
                                        panelWebcam.setImage(image);
1048
                                } catch (InterruptedException e) {
1049
                                        return;
1050
                                } catch (java.security.AccessControlException e) {
1051
                                        csi.warn("Could not load webcam.\n" + e);
1052
                                        return;
1053
                                } catch (IOException e) {
1054
                                        log.append("IOException while trying to load image.");
1055
                                }
1056
                        }
1057
                }
1058

    
1059
        }
1060

    
1061
        /*
1062
        *         RobotIcon class
1063
        *         Provides a means for graphically representing and keeping track of webcam bots.
1064
        */
1065
        class RobotIcon {
1066
                public final int RADIUS = 30;
1067
                public final int CLOSE = 80;
1068

    
1069
                public int x, y;
1070
                public int destx, desty;
1071
                public int id;
1072
                public Color color;
1073
                                public int battery;
1074

    
1075
                public RobotIcon (int id, int x, int y) {
1076
                        this.color = Color.RED;
1077
                        this.x = x;
1078
                        this.y = y;
1079
                        this.id = id;
1080
                        this.destx = -1;
1081
                        this.desty = -1;
1082
                        this.battery = -1;
1083
                }
1084

    
1085
                /**
1086
                *         Relocates this RobotIcon to a new coordinate point.
1087
                *
1088
                */
1089
                public void move (int newX, int newY) {
1090
                        this.x = newX;
1091
                        this.y = newY;
1092
                }
1093

    
1094
                /**
1095
                *         Determines if a given point is within a reasonable range of the current location
1096
                *         to be considered the same robot when moving. The threshold is determined by the
1097
                *         CLOSE value.
1098
                *
1099
                *         @returns Whether or not the given point is reasonably close to the current location.
1100
                *
1101
                */
1102
                public boolean isClose (int nx, int ny) {
1103
                        int dist = (int) Point.distance(this.x, this.y, nx, ny);
1104
                        return (dist < CLOSE);
1105
                }
1106

    
1107
                /**
1108
                *         Determines whether a given point is within the rectangle that circumscribes the
1109
                *         robot's circlular icon. Used for clicking on robots in webcam view.
1110
                                *
1111
                                */
1112
                public boolean contains (int px, int py) {
1113
                                Rectangle rect = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS);
1114
                                return rect.contains(px, py);
1115
                }
1116

    
1117
                public String toString () {
1118
                        String s = "RobotIcon at (" + x + "," + y + "), id " + id;
1119
                        return s;
1120
                }
1121

    
1122
        }
1123

    
1124

    
1125
        /*
1126
        * VectorController class
1127
        * Manages robot motion control graphically
1128
        */
1129
        class VectorController extends GraphicsPanel implements MouseListener, MouseMotionListener {
1130
                int x, y, cx, cy;
1131
                int width, height;
1132
                int side;
1133

    
1134
                public VectorController (Image img) {
1135
                        super (img);
1136
                        width = img.getWidth(null);
1137
                        height = img.getHeight(null);
1138
                        cx = img.getWidth(null)/2;
1139
                        cy = img.getHeight(null)/2;
1140
                        x = cx;
1141
                        y = cy;
1142
                        if (width < height)
1143
                                side = width;
1144
                        else
1145
                                side = height;
1146
                        this.addMouseListener(this);
1147
                        this.addMouseMotionListener(this);
1148
                }
1149

    
1150
                public void setPoint (int x, int y) {
1151
                        if (!isValidPoint(x, y))
1152
                                return;
1153
                        this.x = x;
1154
                        this.y = y;
1155
                        repaint();
1156
                }
1157

    
1158
                public boolean isValidPoint (int x, int y) {
1159
                        double xterm = Math.pow(1.0*(x - cx)/(side/2), 2);
1160
                        double yterm = Math.pow(1.0*(y - cy)/(side/2), 2);
1161
                        return (xterm + yterm <= 1);
1162
                }
1163

    
1164
                public void notifyMouseEvent (MouseEvent e, boolean send) {
1165
                        if (!isValidPoint(e.getX(), e.getY()))
1166
                                return;
1167
                        vectorController.setPoint(e.getX(), e.getY());
1168
                        vectorController.repaint();
1169
                        if (send)
1170
                                vectorController.sendToServer();
1171
                }
1172

    
1173
                public void mouseExited(MouseEvent e) {
1174
                }
1175
                public void mouseEntered(MouseEvent e) {
1176
                }
1177
                public void mouseReleased(MouseEvent e) {
1178
                        this.notifyMouseEvent(e, true);
1179
                }
1180
                public void mouseClicked(MouseEvent e) {
1181
                        this.notifyMouseEvent(e, false);
1182
                }
1183
                public void mousePressed(MouseEvent e) {
1184
                }
1185
                public void mouseDragged(MouseEvent e) {
1186
                        vectorController.notifyMouseEvent(e, false);
1187
                }
1188
                public void mouseMoved(MouseEvent e) {
1189
                }
1190

    
1191
                public int getSpeed () {
1192
                        int dx = x - cx;
1193
                        int dy = y - cy;
1194
                        int v = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) );
1195
                        return v;
1196
                }
1197

    
1198
                /**
1199
                * Returns the angle of the control vector in positive degrees west of north,
1200
                * or negative degrees east of north, whichever is less than or equal to
1201
                * 180 degrees total.
1202
                */
1203
                public int getAngle () {
1204
                        int dx = x - cx;
1205
                        int dy = cy - y;
1206
                        // find reference angle in radians
1207
                        double theta = Math.atan2(Math.abs(dx), Math.abs(dy));
1208
                        // transform to degrees
1209
                        theta = theta * 180 / Math.PI;
1210
                        // adjust for quadrant
1211
                        if (dx < 0 && dy < 0)
1212
                                theta = 90 + theta;
1213
                        else if (dx < 0 && dy >= 0)
1214
                                theta = 90 - theta;
1215
                        else if (dx >= 0 && dy < 0)
1216
                                theta = -90 - theta;
1217
                        else
1218
                                theta = -90 + theta;
1219
                        return (int) theta;
1220
                }
1221

    
1222
                public void paint (Graphics g) {
1223
                        g.setColor(Color.BLACK);
1224
                        g.fillRect(0, 0, width, height);
1225
                        ((Graphics2D)g).setStroke(new BasicStroke(1));
1226
                        g.setColor(Color.RED);
1227
                        g.drawOval(cx-side/2, cy-side/2, side, side);
1228
                        ((Graphics2D)g).setStroke(new BasicStroke(2));
1229
                        g.setColor(Color.GREEN);
1230
                        g.drawLine(cx, cy, x, y);
1231
                        g.fillOval(x-3, y-3, 6, 6);
1232
                }
1233

    
1234
                public void setMaxForward () {
1235
                        setPoint(cx, cy - (side/2) + 1);
1236
                }
1237

    
1238
                public void setMaxReverse () {
1239
                        setPoint(cx, cy + (side/2) - 1);
1240
                }
1241

    
1242
                public void setMaxLeft () {
1243
                        setPoint(cx - (side/2) + 1, cy);
1244
                }
1245

    
1246
                public void setMaxRight () {
1247
                        setPoint(cx + (side/2) - 1, cy);
1248
                }
1249

    
1250
                public void setZero () {
1251
                        setPoint(cx, cy);
1252
                }
1253

    
1254
                public void sendToServer () {
1255
                        System.out.println("Attempting to send angle = " + getAngle() + ", speed = " + getSpeed() + "");
1256
                        String dest = ColonetServerInterface.GLOBAL_DEST;
1257
                        if (cmbRobotNum != null && cmbRobotNum.getSelectedIndex() > 0) {
1258
                                dest = (String)cmbRobotNum.getSelectedItem();
1259
                        }
1260

    
1261
                        if (csi != null) {
1262
                                /*
1263
                                csi.sendData(ColonetServerInterface.MOVE + " " + getSpeed() + " " + getAngle(), dest);
1264
                                */
1265

    
1266
                                //Directional commands
1267
                                if (x > cx && y == cy) {        //move right
1268
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", dest);
1269
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", dest);
1270
                                } else if (x < cx && y == cy) {         //move left
1271
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", dest);
1272
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", dest);
1273
                                } else if (x == cx && y > cy) {         //move forward
1274
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 225", dest);
1275
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 225", dest);
1276
                                } else if (x == cx && y < cy) {         //move backward
1277
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 225", dest);
1278
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 225", dest);
1279
                                } else if (x == cx && y == cy) {        //stop!
1280
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 0", dest);
1281
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 0", dest);
1282
                                }
1283
                        }
1284
                }
1285

    
1286
        }
1287

    
1288
        /*
1289
        * TaskAddWindow class
1290
        * A window that provides a simple way to add tasks to a task queue.
1291
        */
1292
        class TaskAddWindow extends JFrame implements ActionListener, ListSelectionListener {
1293
                JPanel panelButtons;
1294
                JPanel panelParameters;
1295
                JPanel panelSouth;
1296
                JPanel panelSelection;
1297
                JButton btnSubmit;
1298
                JButton btnCancel;
1299
                DefaultListModel availableListModel;
1300
                JList availableList;
1301
                JScrollPane spAvailableTasks;
1302
                JTextArea txtDescription;
1303
                JTextField txtParameters;
1304

    
1305
                public TaskAddWindow () {
1306
                        super("Add a Task");
1307
                        super.setSize(500,500);
1308
                        super.setLayout(new BorderLayout());
1309

    
1310
                        // set up buttons
1311
                        btnSubmit = new JButton("Submit");
1312
                        btnCancel = new JButton("Cancel");
1313
                        panelButtons = new JPanel();
1314
                        panelButtons.setLayout(new FlowLayout());
1315
                        panelButtons.add(btnSubmit);
1316
                        panelButtons.add(btnCancel);
1317
                        this.getRootPane().setDefaultButton(btnSubmit);
1318

    
1319
                        // set up task list
1320
                        availableListModel = new DefaultListModel();
1321
                        availableListModel.addElement("Map the Environment");
1322
                        availableListModel.addElement("Clean Up Chemical Spill");
1323
                        availableListModel.addElement("Grow Plants");
1324
                        availableListModel.addElement("Save the Cheerleader");
1325
                        availableListModel.addElement("Save the World");
1326
                        availableList = new JList(availableListModel);
1327
                        availableList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1328
                        availableList.setSelectedIndex(-1);
1329
                        spAvailableTasks = new JScrollPane(availableList);
1330
                        spAvailableTasks.setBorder(BorderFactory.createTitledBorder("Select A Task"));
1331
                        txtDescription = new JTextArea();
1332
                        txtDescription.setEditable(false);
1333
                        txtDescription.setLineWrap(true);
1334
                        txtDescription.setWrapStyleWord(true);
1335
                        txtDescription.setBorder(BorderFactory.createTitledBorder("Description"));
1336

    
1337
                        //set up parameter area
1338
                        panelParameters = new JPanel();
1339
                        panelParameters.setLayout(new BorderLayout());
1340
                        txtParameters = new JTextField();
1341
                        panelParameters.add(new JLabel("Optional parameters for this task: "), BorderLayout.WEST);
1342
                        panelParameters.add(txtParameters);
1343

    
1344
                        // assemble objects
1345
                        panelSelection = new JPanel();
1346
                        panelSelection.setLayout(new GridLayout(1,2));
1347
                        panelSelection.add(spAvailableTasks);
1348
                        panelSelection.add(txtDescription);
1349

    
1350
                        panelSouth = new JPanel();
1351
                        panelSouth.setLayout(new GridLayout(2,1));
1352
                        panelSouth.add(panelParameters);
1353
                        panelSouth.add(panelButtons);
1354

    
1355
                        this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
1356
                        this.getContentPane().add(panelSelection, BorderLayout.CENTER);
1357
                        this.setLocationRelativeTo(null);
1358

    
1359
                        // add listeners here
1360
                        availableList.addListSelectionListener(this);
1361
                        btnSubmit.addActionListener(this);
1362
                        btnCancel.addActionListener(this);
1363
                }
1364

    
1365
                public void prompt () {
1366
                        this.setVisible(true);
1367
                }
1368

    
1369
                private String getDescription (int index) {
1370
                        if (index < 0)
1371
                                return "";
1372
                        switch (index) {
1373
                                case 0: return "SLAM and junk";
1374
                                case 1: return "I'm not sure this works";
1375
                                case 2: return "Push them into the light";
1376
                                case 3: return "...";
1377
                                case 4: return "...";
1378

    
1379
                                default: return "Task not recognized";
1380
                        }
1381
                }
1382

    
1383
                public void actionPerformed (ActionEvent e) {
1384
                        Object source = e.getSource();
1385
                        if (source == btnSubmit) {
1386
                                txtParameters.setText(txtParameters.getText().trim());
1387

    
1388

    
1389
                                this.setVisible(false);
1390
                        } else if (source == btnCancel) {
1391
                                this.setVisible(false);
1392
                        }
1393
                }
1394

    
1395
                public void valueChanged (ListSelectionEvent e) {
1396
                        int index = availableList.getSelectedIndex();
1397
                        if (index >= 0)
1398
                                txtDescription.setText(getDescription(index));
1399
                }
1400

    
1401
        }
1402

    
1403
        /*
1404
        *         BatteryIcon class
1405
        *         Graphical representation of battery level
1406
        */
1407
        class BatteryIcon implements Icon {
1408
                private int width;
1409
                        private int height;
1410
                        private int level;
1411

    
1412
                /**
1413
                * Constructs a new BatteryIcon with all default parameters.
1414
                * Default width and height are 50.
1415
                * Default level is 100.
1416
                */
1417
                        public BatteryIcon(){
1418
                                this(100, 50, 50);
1419
                        }
1420

    
1421
                /**
1422
                * Constructs a new BatteryIcon with default width and height, and with the specified level.
1423
                * Default width and height are 50.
1424
                */
1425
                        public BatteryIcon(int startLevel){
1426
                                this(startLevel, 50, 50);
1427
                        }
1428

    
1429
                /**
1430
                * Constructs a new BatteryIcon with the specified level, width, and height.
1431
                */
1432
                        public BatteryIcon(int startLevel, int w, int h){
1433
                                level = startLevel;
1434
                                width = w;
1435
                                height = h;
1436
                        }
1437

    
1438
                        public void paintIcon(Component c, Graphics g, int x, int y) {
1439
                                        Graphics2D g2d = (Graphics2D) g.create();
1440
                                        //clear the background
1441
                                        g2d.setColor(Color.WHITE);
1442
                                        g2d.fillRect(x + 1, y + 1, width - 2, height - 2);
1443
                                        //outline
1444
                                        g2d.setColor(Color.BLACK);
1445
                                        g2d.drawRect((int)(x + width*.3), y + 2, (int)(width*.4), height - 4);
1446
                                        //battery life rectangle
1447
                        if (level > 50)
1448
                                g2d.setColor(Color.GREEN);
1449
                        else if (level > 25)
1450
                                g2d.setColor(Color.YELLOW);
1451
                        else
1452
                                g2d.setColor(Color.RED);
1453
                                        int greenX = (int)(x + 1 + width*.3);
1454
                                        int greenY = (int)((y+3) + Math.abs(level-100.0)*(height-6)/(100));
1455
                                        int greenWidth = (int)(width*.4 - 2)+1;
1456
                                        int greenHeight = 1+(int)(level-0.0)*(height-6)/(100);
1457
                                        g2d.fillRect(greenX, greenY, greenWidth, greenHeight);
1458
                                        //text
1459
                                        g2d.setColor(Color.BLACK);
1460
                                        g2d.drawString(level + "%", greenX + greenWidth/2 - 10, greenY + greenHeight/2 + 5);
1461

    
1462
                                        g2d.dispose();
1463
                        }
1464

    
1465
                /**
1466
                * Sets the battery level for this BatteryIcon. The level should be given in raw form, i.e. 0-255 directly
1467
                * from the robot. The value will be converted to a representative percentage automatically.
1468
                *
1469
                * @param newLevel the new battery reading from the robot that this BatteryIcon will display.
1470
                */
1471
                        public void setLevel(int newLevel) {
1472
                                level = convert(newLevel);
1473
                                repaint();
1474
                                System.out.println("Updated level to " + level);
1475
                        }
1476

    
1477
                        public int getIconWidth() {
1478
                                        return width;
1479
                        }
1480

    
1481
                        public int getIconHeight() {
1482
                                        return height;
1483
                        }
1484

    
1485
                /**
1486
                * Converts a robot battery reading into representable form.
1487
                * Readings from the robot are returned as raw values, 0-255. This method converts the reading into a value
1488
                * from 0 to 100 so that the practical remaining charge is represented.
1489
                *
1490
                * @param level The battery level as returned by the robot.
1491
                * @returns The representable battery percentage.
1492
                */
1493
                private int convert (int level) {
1494
                        // TODO: make this a forreals conversion.
1495
                        return (int) (100.0 * level / 170);
1496
                }
1497

    
1498
        }
1499

    
1500

    
1501

    
1502
}