Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (44.3 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
/**
17
*        The Colonet Graphical User Interface Applet for use locally and over an internet connection.
18
*        @author Gregory Tress
19
*        
20
*        To generate javadoc on this file or other java files, use javadoc *.java -d doc, where doc
21
*        is the name of the folder into which the files should be written.
22
*/
23
public class Colonet extends JApplet implements ActionListener, MouseInputListener, KeyListener, Runnable {
24

    
25
        // Used for images
26
        final int CANVAS_SIZE = 500;  //the applet may be slow if the canvas gets too large
27
        final int BUFFER = 50;
28
        final int RADIUS = 30;
29
        
30
        //Used for the robot controller
31
        final int VECTOR_CONTROLLER_HEIGHT = 220;
32
        final int VECTOR_CONTROLLER_WIDTH = 350;
33
        
34

    
35
        // Connection
36
        JTextField txtHost;                                
37
        JTextField txtPort;                                
38
        JButton btnConnect;        
39
        JButton btnGetXBeeIDs;
40
        JLabel lblConnectionStatus;
41
        JTextArea txtMatrix;
42
        JTextArea txtInfo; 
43
        JPanel panelConnect;
44
        JPanel panelServerInterface;
45
        Socket socket;                                        
46
        OutputStreamWriter out;
47
        DataUpdater dataUpdater;  
48
        
49
        // South
50
        JPanel panelSouth;
51
        JTextArea log;
52
        JScrollPane spLog;
53
        
54
        // Control
55
        JPanel panelControl;
56
        JTabbedPane tabPaneControl;
57
        JPanel panelRobotControl;
58
        JPanel panelRobotDirection;
59
        JPanel panelRobotDirectionButtons;
60
        JPanel panelRobotCommands;
61
        JButton btnF, btnB, btnL, btnR, btnActivate;
62
        JComboBox cmbRobotNum;
63
        JLabel lblBattery;
64
        JLabel lblSelected;
65
        BatteryIcon batteryIcon;
66
        JPanel panelBattery;
67
        VectorController vectorController;
68
        BufferedImage imageVectorControl;
69
        JButton btnAssignID;
70
        boolean setWaypoint;
71
        int setWaypointID;
72
        JButton btnCommand_MoveTo;
73
        JButton btnCommand_MoveAll;
74
        JButton btnCommand_StopTask;
75
        JButton btnCommand_ResumeTask;
76
        JButton btnCommand_ChargeNow;
77
        JButton btnCommand_StopCharging;
78
        
79
        // Task Manager
80
        JPanel panelTaskManager;
81
        JScrollPane spTaskManager;
82
        JPanel panelTaskManagerControls;
83
        JPanel panelTaskManagerControlsPriority;
84
        DefaultListModel taskListModel;
85
        JList taskList;
86
        JButton btnAddTask;
87
        JButton btnRemoveTask;
88
        JButton btnMoveTaskUp;
89
        JButton btnMoveTaskDown;
90
        JButton btnUpdateTasks;
91
        TaskAddWindow taskAddWindow;
92
        
93
        //Webcam
94
        WebcamPanel panelWebcam;
95
        GraphicsPanel panelGraph;
96
        GraphicsConfiguration gc;
97
        volatile BufferedImage image;
98
        volatile Graphics2D canvas;
99
        int cx, cy;
100
        JTabbedPane tabPaneMain;
101
        
102
        Font botFont;
103
        volatile int numBots;
104
        volatile int selectedBot;  //the user has selected this bot graphically
105
        volatile java.util.List <RobotIcon> robotIcons;  //contains boundary shapes around bots for click detection
106
        volatile int[] xbeeID;
107
        
108
        Colonet self = this;
109
        Thread paintThread;
110
        WebcamLoader webcamLoader;
111
        ColonetServerInterface csi;
112

    
113
        
114
        public void init () {
115
                // Set the default look and feel - choose one
116
        //String laf = UIManager.getSystemLookAndFeelClassName();
117
                String laf = UIManager.getCrossPlatformLookAndFeelClassName();
118
                //String laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
119
        try {
120
            UIManager.setLookAndFeel(laf);
121
        } catch (UnsupportedLookAndFeelException exc) {
122
            System.err.println ("Warning: UnsupportedLookAndFeel: " + laf);
123
        } catch (Exception exc) {
124
            System.err.println ("Error loading " + laf + ": " + exc);
125
        }
126
                // We should invoke and wait to avoid browser display difficulties
127
                Runnable r = new Runnable() {
128
                        public void run() {
129
                                createAndShowGUI();
130
                        }
131
                };
132
                try {
133
                        SwingUtilities.invokeAndWait(r);
134
                } catch (InterruptedException e) {
135
                        //Not really sure why we would be in this situation
136
                        System.out.println("InterruptedException in init: " + e);
137
                } catch (java.lang.reflect.InvocationTargetException e) {
138
                        //This could happen for various reasons if there is a problem in createAndShowGUI
139
                        e.printStackTrace();
140
                }
141
        }
142
        
143
        public void destroy () {
144
                try { paintThread.interrupt(); } catch (Exception e) { }
145
        }
146

    
147
        private synchronized void createAndShowGUI () {
148
                // init graphical elements
149
                // Get the graphics configuration of the screen to create a buffer
150
                gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
151
                        .getDefaultScreenDevice().getDefaultConfiguration();
152
                image = gc.createCompatibleImage(CANVAS_SIZE,CANVAS_SIZE);
153
                canvas = image.createGraphics();
154
                canvas.setStroke(new BasicStroke(2));  //set pen width
155
                panelGraph = new GraphicsPanel(image, false);  //set automatic double-buffering to false. we are doing it manually.
156
                panelWebcam = new WebcamPanel();
157
                tabPaneMain = new JTabbedPane();
158
                tabPaneMain.add(panelWebcam, "Webcam");
159
                //tabPaneMain.add(panelGraph, "Graph");
160
                
161
                // Calculate center of canvas
162
                cx = image.getWidth() / 2;
163
                cy = image.getHeight() / 2;
164
                
165
                // Set up robots
166
                botFont = new Font("Arial", Font.PLAIN, 14);
167
                numBots = 0;
168
                selectedBot = -1;
169
                robotIcons = new ArrayList <RobotIcon> ();
170
                
171
                // Connection area
172
                txtMatrix = new JTextArea();
173
                txtMatrix.setBorder(BorderFactory.createTitledBorder("Info"));
174
                txtInfo = new JTextArea();
175
                txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
176
                txtInfo.setEditable(false);
177
                txtHost = new JTextField(this.getDocumentBase().getHost());
178
                txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
179
                txtPort = new JTextField("10123");
180
                txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
181
                btnConnect = new JButton("Connect");
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
                btnF = new JButton("^");
201
                btnB = new JButton("v");
202
                btnL = new JButton("<");
203
                btnR = new JButton(">");
204
                btnActivate = new JButton("o");
205
                panelRobotDirectionButtons.setLayout(new GridLayout(1,5));
206
                panelRobotDirectionButtons.add(btnActivate);
207
                panelRobotDirectionButtons.add(btnF);
208
                panelRobotDirectionButtons.add(btnB);
209
                panelRobotDirectionButtons.add(btnL);
210
                panelRobotDirectionButtons.add(btnR);
211
                
212
                imageVectorControl = gc.createCompatibleImage(VECTOR_CONTROLLER_WIDTH, VECTOR_CONTROLLER_HEIGHT);
213
                vectorController = new VectorController(imageVectorControl);
214
                panelRobotDirection.setLayout(new BorderLayout());
215
                panelRobotDirection.add(vectorController, BorderLayout.CENTER);
216
                panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
217
                
218
                // Robot Control and Commands
219
                panelRobotCommands = new JPanel();
220
                panelRobotCommands.setLayout(new GridLayout(5,2));
221
                cmbRobotNum = new JComboBox();
222
                // Battery subset
223
                batteryIcon = new BatteryIcon(0);
224
                lblBattery = new JLabel(batteryIcon);
225
                lblSelected = new JLabel("None");
226
                // Command subset
227
                setWaypoint = false;
228
                setWaypointID = -1;
229
                btnAssignID = new JButton("Assign ID");
230
                btnCommand_MoveTo = new JButton("Move to ...");
231
                btnCommand_MoveAll = new JButton("Move all ...");
232
                btnCommand_StopTask = new JButton("Stop Current Task");
233
                btnCommand_ResumeTask = new JButton("Resume Current Task");
234
                btnCommand_ChargeNow = new JButton("Recharge Now");
235
                btnCommand_StopCharging = new JButton("Stop Recharging");
236
                panelRobotCommands.add(new JLabel("Select Robot to Control: "));
237
                panelRobotCommands.add(cmbRobotNum);
238
                panelRobotCommands.add(new JLabel("Battery Level: "));
239
                panelRobotCommands.add(lblBattery);
240
                panelRobotCommands.add(new JLabel("Selected Icon: "));
241
                panelRobotCommands.add(lblSelected);
242
                panelRobotCommands.add(btnAssignID);
243
                panelRobotCommands.add(new JLabel(""));
244
                panelRobotCommands.add(btnCommand_MoveTo);
245
                panelRobotCommands.add(btnCommand_MoveAll);
246
                //panelRobotCommands.add(btnCommand_StopTask);
247
                //panelRobotCommands.add(btnCommand_ResumeTask);
248
                //panelRobotCommands.add(btnCommand_ChargeNow);
249
                //panelRobotCommands.add(btnCommand_StopCharging);
250
                panelRobotControl = new JPanel();
251
                panelRobotControl.setLayout(new GridLayout(2,1));
252
                panelRobotControl.add(panelRobotDirection);
253
                panelRobotControl.add(panelRobotCommands);
254
                
255
                
256
                // Task Manager
257
                panelTaskManager = new JPanel();
258
                panelTaskManager.setLayout(new BorderLayout());
259
                taskListModel = new DefaultListModel();
260
                taskList = new JList(taskListModel);
261
                taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
262
                taskList.setSelectedIndex(0);
263
                spTaskManager = new JScrollPane(taskList);
264
                panelTaskManagerControls = new JPanel();
265
                panelTaskManagerControls.setLayout(new GridLayout(1,4));
266
                panelTaskManagerControlsPriority = new JPanel();
267
                panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
268
                btnAddTask = new JButton("Add...");
269
                btnRemoveTask = new JButton("Remove");
270
                btnMoveTaskUp = new JButton("^");
271
                btnMoveTaskDown = new JButton("v");
272
                btnUpdateTasks = new JButton("Update");
273
                panelTaskManagerControlsPriority.add(btnMoveTaskUp);
274
                panelTaskManagerControlsPriority.add(btnMoveTaskDown);
275
                panelTaskManagerControls.add(btnAddTask);
276
                panelTaskManagerControls.add(btnRemoveTask);
277
                panelTaskManagerControls.add(btnUpdateTasks);
278
                panelTaskManagerControls.add(panelTaskManagerControlsPriority);
279
                panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
280
                panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
281
                panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
282
                taskAddWindow = new TaskAddWindow();
283
                
284
                // Message log
285
                log = new JTextArea();
286
                spLog = new JScrollPane(log,
287
                        ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 
288
                        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
289
                spLog.setBorder(BorderFactory.createTitledBorder("Log"));
290
                spLog.setPreferredSize(new Dimension(0, 120));
291
                log.setEditable(false);
292
                
293
                // Main control mechanism
294
                panelControl = new JPanel();
295
                panelControl.setLayout(new GridLayout(1,1));
296
                tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
297
                tabPaneControl.setPreferredSize(new Dimension(VECTOR_CONTROLLER_WIDTH, 0));
298
                tabPaneControl.addTab("Connection", panelServerInterface);
299
                tabPaneControl.addTab("Robots", panelRobotControl);
300
                //tabPaneControl.addTab("Tasks", panelTaskManager);
301
                panelControl.add(tabPaneControl);
302
                
303
                // Set up elements in the south
304
                panelSouth = new JPanel();
305
                panelSouth.setLayout(new GridLayout(1,2));
306
                //panelSouth.add(spLog);
307

    
308
                // Put all elements in the ContentPane
309
                this.getContentPane().setLayout(new BorderLayout());
310
                this.getContentPane().add(tabPaneMain, BorderLayout.CENTER);
311
                this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
312
                this.getContentPane().add(panelControl, BorderLayout.EAST);
313
                this.setVisible(true);
314
                
315
                /* Add all listeners here */
316
                // Task Management
317
                btnAddTask.addActionListener(this);
318
                btnRemoveTask.addActionListener(this);
319
                btnMoveTaskUp.addActionListener(this);
320
                btnMoveTaskDown.addActionListener(this);
321
                btnUpdateTasks.addActionListener(this);
322
                // Robot Control
323
                btnF.addActionListener(this);
324
                btnB.addActionListener(this);
325
                btnL.addActionListener(this);
326
                btnR.addActionListener(this);
327
                btnF.addKeyListener(this);
328
                btnB.addKeyListener(this);
329
                btnL.addKeyListener(this);
330
                btnR.addKeyListener(this);
331
                btnActivate.addActionListener(this);
332
                btnActivate.addKeyListener(this);
333
                cmbRobotNum.addKeyListener(this);
334
                btnCommand_MoveTo.addActionListener(this);
335
                btnCommand_MoveAll.addActionListener(this);
336
                btnCommand_StopTask.addActionListener(this);
337
                btnCommand_ResumeTask.addActionListener(this);
338
                btnCommand_ChargeNow.addActionListener(this);
339
                btnCommand_StopCharging.addActionListener(this);
340
                // Other
341
                btnConnect.addActionListener(this);
342
                btnGetXBeeIDs.addActionListener(this);
343
                btnAssignID.addActionListener(this);
344
                panelWebcam.addMouseListener(this);        
345
        
346
        }
347
        
348
        public void run () {
349
                while (true) {
350
                        repaint();
351
                        try { 
352
                                Thread.sleep(90);
353
                        } catch (InterruptedException e) {
354
                                return;
355
                        }
356
                }
357
        }
358
        
359
        public void paint (Graphics g) {
360
            super.paint(g);
361
        }
362
        
363
        public void update (Graphics g) {
364
            paint(g);
365
        }
366
                
367
        /** 
368
        * Gets the JTextArea used for storing the activity log. This method returns a reference to the 
369
        * JTextArea that stores the log. The log can contain any activity that is revelant to the use
370
        * of the applet, and may optionally display debugging information.
371
        *
372
        * @return the JTextArea where BOM matrix information is stored.
373
        */ 
374
        public JTextArea getLog () {
375
                return log;
376
        }
377
        
378
        /** 
379
        * Gets the JTextArea used for storing the BOM matrix data. This method returns a reference to the 
380
        * JTextArea that stores the BOM matrix. The values in the matrix are stored as integers separated 
381
        * by spaces, and the lines should be separated by a newline.
382
        * 
383
        * @return the JTextArea where BOM matrix information is stored.
384
        */
385
        public JTextArea getMatrixInput () {
386
                return txtMatrix;
387
        }
388
        
389
        /**
390
        * Parses a String containing BOM matrix information.
391
        * The ColonetServerInterface receives lines of the BOM matrix.  (For encoding 
392
        * information, see the ColonetServerInterface documentation.)  The entire matrix is passed
393
        * to the client when requested. This method takes a string of the form 
394
        * "[command code] [command code] [number of robots] [data0] [data1] ..."
395
        * with tokens separated by spaces and containing no brackets.  
396
        * The [command code]s are predefined values identifying this String as a BOM data
397
        * String, [number of robots] is an integer, and the values that follow are 
398
        * the sensor readings of the robots in order, starting with robot 0.  Only [number of robots]^2
399
        * data entries will be read.  The matrix values are saved locally until the next String is parsed.
400
        * 
401
        *
402
        * @param line the String containing BOM matrix information.
403
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots]^2 data entries in the String
404
        */
405
        public void parseMatrix (String line) {
406
                txtMatrix.setText("");
407
                String [] str = line.split(" ");
408
                int num = Integer.parseInt(str[2]);
409
                for (int i = 0; i < num; i++) {
410
                        for (int j = 0; j < num; j++) {
411
                                String next = str[3 + i*num + j];
412
                                if (next.equals("-1"))
413
                                        txtMatrix.append("-");
414
                                else 
415
                                        txtMatrix.append(next);
416
                                if (j < num - 1) 
417
                                        txtMatrix.append(" ");
418
                        }
419
                        if (i < num - 1) 
420
                                txtMatrix.append("\n");
421
                }
422
                repaint();
423
        }
424
        
425
        public void connect () {
426
            lblConnectionStatus.setText("Status: Connecting...");
427
            webcamLoader = new WebcamLoader(this);
428
                dataUpdater = new DataUpdater();
429
                csi = new ColonetServerInterface(this);
430
                csi.connect(txtHost.getText(), txtPort.getText());
431
                if (!csi.isReady()) {
432
                    lblConnectionStatus.setText("Status: Offline");
433
                        return;
434
            }
435
                btnConnect.setText("Disconnect");
436
                lblConnectionStatus.setText("Status: Connected");
437
                dataUpdater.start();
438
                webcamLoader.start();
439
        }
440
        
441
        public void disconnect () {
442
            lblConnectionStatus.setText("Status: Disconnecting...");
443
            dataUpdater.interrupt();
444
            csi.disconnect();
445
            csi = null;
446
            btnConnect.setText("Connect");
447
            lblConnectionStatus.setText("Status: Disconnected");
448
        }
449
        
450
        /**
451
        * Parses a String containing a task queue update.
452
        * Format is currently not specified.
453
        * This method currently does nothing.
454
        *
455
        * @param line the String containing task queue update information.
456
        */
457
        public void parseQueue (String line) {
458
                log.append("Got queue update\n");
459
                //TODO: display new queue data in tasks tab
460
        }
461
        
462
        /**
463
        * Parses a String containing XBee ID values.
464
        * The ColonetServerInterface receives Strings of XBee information.  (For encoding 
465
        * information, see the ColonetServerInterface documentation.)  This method takes
466
        * a string of the form "[command code] [command code] [number of robots] [id0] [id1] ..."
467
        * with tokens separated by spaces and containing no brackets.  
468
        * The [command code]s are predefined values identifying this String as an XBee
469
        * ID String, [number of robots] is an integer, and the values that follow are 
470
        * the IDs of the robots in order, starting with robot 0.  Only [number of robots] 
471
        * will be read.  The ID values are saved locally until the next String is parsed.
472
        * The purpose of having this list is to ensure that robots are properly identified for control purposes.
473
        * This keeps robot identification consistent between sessions and prevents arbitrary assignment. 
474
        *
475
        * @param line the String containing XBee ID information.
476
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots] IDs in the String
477
        * @see ColonetServerInterface#sendXBeeIDRequest()
478
        */
479
        public void parseXBeeIDs (String line) {
480
                String [] str = line.split(" ");
481
                int num = Integer.parseInt(str[2]);
482
                xbeeID = new int[num];
483
                for (int i = 0; i < num; i++)
484
                        xbeeID[i] = Integer.parseInt(str[i+3]);
485
                
486
                //update the list of robots to control
487
                //but save the old value first
488
                Object oldSelection = cmbRobotNum.getSelectedItem();
489
                cmbRobotNum.removeAllItems();
490
                cmbRobotNum.addItem(new String("   All   "));
491
                for (int i = 0; i < num; i++)
492
                        cmbRobotNum.addItem(new String("" + xbeeID[i]));
493
                cmbRobotNum.setSelectedItem(oldSelection);
494
                repaint();
495
        }
496
        
497
        /**
498
        * Parses a String containing battery information.
499
        * The ColonetServerInterface receives Strings of battery information.  (For encoding 
500
        * information, see the ColonetServerInterface documentation.)  This method takes
501
        * a string of the form "[command code] [command code] [robot ID] [value]"
502
        * with tokens separated by spaces and containing no brackets.  
503
        * The [command code]s are predefined values identifying this String as a battery
504
        * information String, [robot ID] is an integer, and [value] is a battery measurement.
505
        * This updates the batery information for a single robot.
506
        * 
507
        *
508
        * @param line the String containing battery information.
509
        * @see ColonetServerInterface#sendBatteryRequest(int)
510
        */
511
        public void parseBattery (String line) {
512
                String [] str = line.split(" ");
513
                int botNum = Integer.parseInt(str[2]);
514
                int level = Integer.parseInt(str[3]);
515
                int selected = -1;
516
                try { 
517
                    selected = Integer.parseInt((String)cmbRobotNum.getSelectedItem());
518
                } catch (Exception e) {
519
                }
520
                if (selected == botNum) {
521
                        batteryIcon.setLevel(level);
522
                }
523
                repaint();
524
        }
525
        
526
        /**
527
        * Parses a String containing visual robot position information along with 
528
        * canonical ID assignments.
529
        */
530
        public void parsePositions (String line) {
531
                String [] str = line.split(" ");
532
                java.util.List <RobotIcon> newList = new ArrayList <RobotIcon> ();
533
                
534
                for (int i = 2; i < str.length; i+=3) {
535
                        int id = Integer.parseInt(str[i]);
536
                        int x = Integer.parseInt(str[i+1]);
537
                        int y = Integer.parseInt(str[i+2]);
538
                        RobotIcon newIcon = new RobotIcon(id, x, y);
539
                        newList.add(newIcon);
540
                }
541
                robotIcons = newList;
542
                repaint();
543
        }
544
        
545
        
546
        //
547
        // MouseListener methods
548
        //
549
        public void mousePressed(MouseEvent e) {
550
            //Start a new Thread to handle the MouseEvent
551
            (new MouseHandler(e)).start();
552
        }
553
        public void mouseExited(MouseEvent e) {
554
        }
555
        public void mouseEntered(MouseEvent e) {
556
        }
557
        public void mouseReleased(MouseEvent e) {
558
        }
559
        public void mouseClicked(MouseEvent e) {
560
        }
561
        public void mouseDragged(MouseEvent e) {
562
        }
563
        public void mouseMoved(MouseEvent e) {
564
        }
565
        
566
        //
567
        // KeyListener methods
568
        //
569
        public void keyPressed (KeyEvent e) {
570
                //Start a new Thread to handle the KeyEvent
571
            (new KeyHandler(e)).start();
572
        }
573
        public void keyReleased (KeyEvent e) {
574
        }
575
        public void keyTyped (KeyEvent e) {
576
        }
577
        
578
        //
579
        // ActionListener method
580
        //
581
        public void actionPerformed (ActionEvent e) {
582
                // Start a new Thread to handle the ActionEvent
583
                (new ActionHandler(e)).start();
584
        }
585
        
586
        class MouseHandler extends Thread {
587
            
588
            MouseEvent e;
589
            
590
            public MouseHandler (MouseEvent event) {
591
                super("MouseHandler");
592
                this.e = event;
593
            }
594
        
595
            public void run () {
596
                
597
                // If we are selecting a waypoint (destination) for a specific bot
598
                if (setWaypoint && setWaypointID  >= 0) {
599
                    setWaypoint = false;
600
                    panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
601
                    if (selectedBot < 0)
602
                        return;
603
                    
604
                    RobotIcon r = robotIcons.get(selectedBot);
605
                    r.destx = e.getX();
606
                    r.desty = e.getY();
607
                    
608
                    if (csi != null)
609
                        csi.sendAbsoluteMove(r.id, r.destx, r.desty);
610
                    
611
                    return;
612
                }
613
                
614
                // Right-click also means we are moving a robot
615
                if (e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3) {
616
                    if (selectedBot < 0)
617
                        return;
618
                    
619
                    RobotIcon r = robotIcons.get(selectedBot);
620
                    r.destx = e.getX();
621
                    r.desty = e.getY();
622
                    
623
                    if (csi != null)
624
                        csi.sendAbsoluteMove(r.id, r.destx, r.desty);
625
                    
626
                    return;
627
                }
628
                
629
                // If we are setting all waypoints
630
                if (setWaypoint) {
631
                    setWaypoint = false;
632
                    panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
633
                    for (int i = 0; i < robotIcons.size(); i++) {
634
                        RobotIcon r = robotIcons.get(i);
635
                        r.destx = e.getX();
636
                        r.desty = e.getY();
637
                    }
638
                    return;
639
                }
640
                
641
                // Otherwise, we are selecting a bot, or doing nothing
642
                    for (int i = 0; i < robotIcons.size(); i++) {
643
                        RobotIcon r = robotIcons.get(i);
644
                        if (r.contains(e.getX(), e.getY())) {
645
                            selectedBot = i;
646
                            lblSelected.setText(" " + r.id);
647
                                        // Try to select the clicked bot, if its XBee ID is detected.
648
                                        for (int j = 1; j < cmbRobotNum.getItemCount(); j++) {
649
                                                if (Integer.parseInt(cmbRobotNum.getItemAt(j).toString()) == selectedBot)
650
                                                        cmbRobotNum.setSelectedIndex(j);
651
                                        }
652
                                        return;
653
                        }
654
                    }
655
                    
656
                    repaint();
657
            }
658
        }
659
        
660
        class KeyHandler extends Thread {
661
        
662
                KeyEvent e;
663
                
664
                public KeyHandler (KeyEvent event) {
665
                        super("KeyHandler");
666
                        this.e = event;
667
                }
668
                
669
                public void run () {
670
                        int code = e.getKeyCode();
671
                        if (code == KeyEvent.VK_UP) {
672
                                vectorController.setMaxForward();
673
                                vectorController.sendToServer();
674
                        } else if (code == KeyEvent.VK_DOWN) {
675
                                vectorController.setMaxReverse();
676
                                vectorController.sendToServer();
677
                        } else if (code == KeyEvent.VK_LEFT) {
678
                                vectorController.setMaxLeft();
679
                                vectorController.sendToServer();
680
                        } else if (code == KeyEvent.VK_RIGHT) {
681
                                vectorController.setMaxRight();
682
                                vectorController.sendToServer();
683
                        } else if (code == KeyEvent.VK_S) {
684
                                vectorController.setZero();
685
                                vectorController.sendToServer();
686
                        }
687
                        repaint();
688
                }
689
        }
690
        
691
        class ActionHandler extends Thread {
692
        
693
            ActionEvent e;
694
        
695
            public ActionHandler (ActionEvent event) {
696
                super("ActionHandler");
697
                this.e = event;
698
            }
699
            
700
            public void run () {
701
                Object source = e.getSource();
702
                
703
                    // General Actions
704
                    if (source == btnConnect) {
705
                        if (csi == null)
706
                                connect();
707
                            else
708
                                disconnect();
709
                    } else if (source == btnGetXBeeIDs) {
710
                            csi.sendXBeeIDRequest();
711
                    } else if (source == btnAssignID) {
712
                        String message;
713
                        if (selectedBot < 0)
714
                            return;
715
                        int curID = robotIcons.get(selectedBot).id;
716
                        if (curID < 0)
717
                            message = "That robot is unidentified. Please specify its ID.";
718
                        else
719
                            message = "That robot has ID " + curID + ". You may reassign it now.";
720
                        String result = JOptionPane.showInputDialog(self, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
721
                        if (result == null)
722
                            return;
723
                    int newID = -1;
724
                        try {
725
                            newID = Integer.parseInt(result);
726
                        } catch (Exception ex) {
727
                            csi.warn("Invalid ID.");
728
                            return;
729
                        }
730
                        // Assign new ID and update display  
731
                            csi.sendIDAssignment(curID, newID);
732
                        robotIcons.get(selectedBot).id = newID;
733
                        robotIcons.get(selectedBot).color = Color.GREEN;
734
                        lblSelected.setText(" " + newID);
735
                        
736
                        
737
                    }
738
                
739
                    // Robot Movement Controls
740
                    else if (source == btnF) {
741
                            vectorController.setMaxForward();
742
                            vectorController.sendToServer();
743
                    } else if (source == btnB) {
744
                            vectorController.setMaxReverse();
745
                            vectorController.sendToServer();
746
                    } else if (source == btnL) {
747
                            vectorController.setMaxLeft();
748
                            vectorController.sendToServer();
749
                    } else if (source == btnR) {
750
                            vectorController.setMaxRight();
751
                            vectorController.sendToServer();
752
                    } else if (source == btnActivate) {
753
                            vectorController.setZero();
754
                            vectorController.sendToServer();
755
                    }
756
                    // Robot Commands (non-movement)
757
                    else if (source == btnCommand_MoveTo) {
758
                        if (selectedBot < 0)
759
                            return;
760
                        panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
761
                        setWaypoint = true;
762
                        setWaypointID = selectedBot;
763
                                        
764
                    } else if (source == btnCommand_MoveAll) {
765
                        panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
766
                        setWaypoint = true;
767
                        setWaypointID = -1;
768
                                        
769
                    } else if (source == btnCommand_StopTask) {
770
                
771
                    } else if (source == btnCommand_ResumeTask) {
772
                
773
                    } else if (source == btnCommand_ChargeNow) {
774
                
775
                    } else if (source == btnCommand_StopCharging) {
776
                
777
                    }
778
                        
779
                    // Queue Management
780
                    else if (source == btnAddTask) {
781
                            taskAddWindow.prompt();
782
                    } else if (source == btnRemoveTask) {
783
                            if (taskList.getSelectedIndex() >= 0);
784
                                    csi.sendQueueRemove(taskList.getSelectedIndex());
785
                            csi.sendQueueUpdate();
786
                    } else if (source == btnMoveTaskUp) {
787
                            csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
788
                            csi.sendQueueUpdate();
789
                    } else if (source == btnMoveTaskDown) {
790
                            csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
791
                            csi.sendQueueUpdate();
792
                    } else if (source == btnUpdateTasks) {
793
                            csi.sendQueueUpdate();
794
                    }
795
                
796
                repaint();
797
            }
798
        
799
        }
800
        
801
        /*
802
        *        DataUpdater thread.
803
        *   The purpose of this thread is to request data from the server at regular intervals.
804
        *
805
        */
806
        class DataUpdater extends Thread {
807
                final int DATAUPDATER_DELAY = 400;
808
                
809
                public DataUpdater () {
810
                        super("Colonet DataUpdater");
811
                }
812
                
813
                public void run () {
814
                        String line;
815
                        while (true) {
816
                                try {
817
                                        //request more data
818
                                        if (csi != null && csi.isReady()) {
819
                        csi.sendPositionRequest();
820
                                                csi.sendXBeeIDRequest();
821
                                                if (cmbRobotNum.getSelectedIndex() > 0) {
822
                                                    String sel = (String) cmbRobotNum.getSelectedItem();
823
                                                    int num = Integer.parseInt(sel);
824
                                                        csi.sendBatteryRequest(num);
825
                                                }
826
                                        }
827
                                        Thread.sleep(DATAUPDATER_DELAY);
828
                                } catch (InterruptedException e) {
829
                                        return;
830
                                } 
831
                        }
832
                }
833

    
834
        }
835
        
836
        /*
837
        *        GraphicsPanel class
838
        *        An extension of JPanel, designed for holding an image that will be repainted regularly.
839
        */
840
        class GraphicsPanel extends JPanel {
841
                protected Image img;
842
        
843
                public GraphicsPanel (Image img) {
844
                        this(img, true);
845
                }
846
                
847
                public GraphicsPanel (Image img, boolean isDoubleBuffered) {
848
                        super(isDoubleBuffered);
849
                        this.img = img;
850
                }
851
                
852
                public void paint (Graphics g) {
853
                        // Place the buffered image on the screen, inside the panel
854
                        g.drawImage(img, 0, 0, Color.WHITE, this);
855
                }
856
        
857
        }
858
        
859
        /*
860
        *        WebcamPanel class
861
        *        Enables more efficient image handling in a component-controlled environment
862
        */
863
        class WebcamPanel extends JPanel {
864
                int BORDER = 16;  // this is arbitrary. it makes the image look nice inside a border.
865
                int BOT_RADIUS = 40;
866
                volatile BufferedImage img;
867
                BufferedImage buffer;
868
        
869
                public WebcamPanel () {
870
                        super(true);
871
                }
872
                
873
                public synchronized void setImage (BufferedImage newimg) {
874
                        if (img != null) {
875
                                img.flush();
876
                        }
877
                        System.gc();
878
                        img = newimg;
879
                        repaint();
880
                }
881
                                
882
                public synchronized void paint (Graphics g) {
883
                        if (img == null)
884
                                return;
885
                        
886
                        // Calculate scaling
887
                        int maxWidth = getWidth() - 2*BORDER;
888
                        int maxHeight = getHeight() - 2*BORDER;
889
                        double widthRatio = 1.0 * maxWidth / img.getWidth();
890
                        double heightRatio = 1.0 * maxHeight / img.getHeight();
891
                        double scale = 0;
892
                        int newWidth = 0;
893
                        int newHeight = 0;
894
                        int x = 0;
895
                        int y = 0;
896
                        
897
                        if (widthRatio > heightRatio) {  //height is the limiting factor
898
                            scale = heightRatio;
899
                            newHeight = maxHeight;
900
                            newWidth = (int) (img.getWidth() * scale);
901
                            y = BORDER;
902
                            x = (maxWidth - newWidth) / 2 + BORDER;
903
                        } else {  //width is the limiting factor
904
                            scale = widthRatio;
905
                            newWidth = maxWidth;
906
                            newHeight = (int) (img.getHeight() * scale);
907
                            x = BORDER;
908
                            y = (maxHeight - newHeight) / 2 + BORDER;
909
                        }
910
                        
911
                        // Draw everything onto the buffer
912
                        buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
913
                        Graphics2D bufferedGraphics = (Graphics2D)buffer.getGraphics();
914
                        bufferedGraphics.setColor(Color.GRAY);
915
                        bufferedGraphics.fillRect(0, 0, this.getWidth(), this.getHeight());
916
                        Image imgScaled = img.getScaledInstance(newWidth, newHeight, Image.SCALE_FAST);
917
                        bufferedGraphics.drawImage(imgScaled, x, y, this);
918
                        
919
                                                
920
                        // Draw Identifiers and battery levels
921
                        if (robotIcons != null) {
922
                            bufferedGraphics.setStroke(new BasicStroke(2));
923
                            for (int i = 0; i < robotIcons.size(); i++) {
924
                                    RobotIcon r = robotIcons.get(i);
925
                                    bufferedGraphics.setColor(r.color);
926
                                    // Identifier circle
927
                                    int px = (int) (x + r.x * scale);
928
                                    int py = (int) (y + r.y * scale);
929
                                    bufferedGraphics.drawOval(px-RADIUS, py-RADIUS, 2*r.RADIUS, 2*r.RADIUS);
930
                                    // Battery
931
                                    //if (r.battery >= 0) {
932
                                        bufferedGraphics.setColor(Color.GREEN);
933
                                        bufferedGraphics.fillRect(px+20, py+20, 30, 10);
934
                                        bufferedGraphics.setColor(Color.BLACK);
935
                                        bufferedGraphics.drawRect(px+20, py+20, 50, 10);
936
                                    //}
937
                                    // If the robot has a destination, draw the vector
938
                                    if (r.destx >= 0) {
939
                                        bufferedGraphics.drawLine(px, py, (int)(x + r.destx * scale), (int)(y + r.desty * scale));
940
                                    }
941
                            }
942
                        }
943
                        
944
                        // Identify currently-selected robot
945
                        if (selectedBot >= 0) {
946
                            bufferedGraphics.setColor(Color.BLACK);
947
                            RobotIcon r = robotIcons.get(selectedBot);
948
                            int px = (int) (x + r.x * scale);
949
                            int py = (int) (y + r.y * scale);
950
                            bufferedGraphics.drawOval(px-RADIUS-6, py-RADIUS-6, 2*r.RADIUS+12, 2*r.RADIUS+12);
951
                        }
952
                        
953
                        //Display buffered content
954
                        g.drawImage(buffer, 0, 0, this);
955
                }
956
        
957
        }
958
        
959
        /*
960
        *        WebcamLoader class
961
        *        Handles the loading of the webcam image.
962
        */
963
        class WebcamLoader extends Thread 
964
        {
965
                final int WEBCAMLOADER_DELAY = 400;
966
                final String IMAGE_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet.jpg";
967
                                
968
                URL imagePath;
969
                
970
                MediaTracker mt;
971
                BufferedImage image;
972
                Random rand;
973
                
974
                public WebcamLoader (JApplet applet)
975
                {
976
                        super("ColonetWebcamLoader");
977
                        mt = new MediaTracker(applet);
978
                        ImageIO.setUseCache(false);
979
                        rand = new Random();
980
                }
981
                
982
                public void run ()
983
                {
984
                        while (true) {
985
                                try {
986
                                        Thread.sleep(WEBCAMLOADER_DELAY);
987
                                        if (image != null) 
988
                                                image.flush();
989
                                        System.gc();
990
                                        try {
991
                                        imagePath = new URL(IMAGE_PATH + "?rand=" + rand.nextInt(50000));
992
                                } catch (MalformedURLException e) {
993
                                        System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n");
994
                                }
995
                                        image = ImageIO.read(imagePath);
996
                                        // The MediaTracker waitForID pauses the thread until the image is loaded.
997
                                        // We don't want to display a half-downloaded image.
998
                                        mt.addImage(image, 1);
999
                                        mt.waitForID(1);
1000
                                        mt.removeImage(image);
1001
                                        // Save
1002
                                        panelWebcam.setImage(image);
1003
                                } catch (InterruptedException e) {
1004
                                        return;
1005
                                } catch (java.security.AccessControlException e) {
1006
                                        csi.warn("Could not load webcam.\n" + e);
1007
                                        return;
1008
                                } catch (IOException e) {
1009
                                        log.append("IOException while trying to load image.");
1010
                                }
1011
                        }
1012
                }
1013
                
1014
        }
1015
        
1016
        /*
1017
        *  RobotIcon class
1018
        *  Provides a means for graphically representing and keeping track of webcam bots.
1019
        */
1020
        class RobotIcon {
1021
                public final int RADIUS = 30;
1022
                public final int CLOSE = 80;
1023
                
1024
                public int x, y;
1025
                public int destx, desty;
1026
                public int id;
1027
                public Color color;
1028
        public int battery;
1029
                
1030
                public RobotIcon (int id, int x, int y) {
1031
                        this.color = Color.RED;
1032
                        this.x = x;
1033
                        this.y = y;
1034
                        this.id = id;
1035
                        this.destx = -1;
1036
                        this.desty = -1;
1037
                        this.battery = -1;
1038
                }
1039
                
1040
                /**
1041
                *  Relocates this RobotIcon to a new coordinate point.
1042
                *
1043
                */
1044
                public void move (int newX, int newY) {
1045
                        this.x = newX;
1046
                        this.y = newY;
1047
                }
1048
                
1049
                /**
1050
                *  Determines if a given point is within a reasonable range of the current location
1051
                *  to be considered the same robot when moving. The threshold is determined by the 
1052
                *  CLOSE value.
1053
                *
1054
                *  @returns Whether or not the given point is reasonably close to the current location.
1055
                *
1056
                */
1057
                public boolean isClose (int nx, int ny) {
1058
                        int dist = (int) Point.distance(this.x, this.y, nx, ny);
1059
                        return (dist < CLOSE);
1060
                }
1061
                
1062
                /**
1063
                *  Determines whether a given point is within the rectangle that circumscribes the
1064
                *  robot's circlular icon. Used for clicking on robots in webcam view.
1065
        *
1066
        */
1067
                public boolean contains (int px, int py) {
1068
                    Rectangle rect = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS);
1069
                    return rect.contains(px, py);
1070
                }
1071
                
1072
                public String toString () {
1073
                        String s = "RobotIcon at (" + x + "," + y + "), id " + id;
1074
                        return s;
1075
                }
1076
                
1077
        }
1078

    
1079
        
1080
        /*
1081
        *        VectorController class
1082
        *        Manages robot motion control graphically
1083
        */
1084
        class VectorController extends GraphicsPanel implements MouseListener, MouseMotionListener {
1085
                int x, y, cx, cy;
1086
                int width, height;
1087
                int side;
1088
                
1089
                public VectorController (Image img) {
1090
                        super (img);
1091
                        width = img.getWidth(null);
1092
                        height = img.getHeight(null);
1093
                        cx = img.getWidth(null)/2;
1094
                        cy = img.getHeight(null)/2;
1095
                        x = cx;
1096
                        y = cy;
1097
                        if (width < height)
1098
                                side = width;
1099
                        else
1100
                                side = height;
1101
                        this.addMouseListener(this);
1102
                        this.addMouseMotionListener(this);
1103
                }
1104
                
1105
                public void setPoint (int x, int y) {
1106
                        if (!isValidPoint(x, y))
1107
                                return;
1108
                        this.x = x;
1109
                        this.y = y;
1110
                        repaint();
1111
                }
1112
                
1113
                public boolean isValidPoint (int x, int y) {
1114
                        double xterm = Math.pow(1.0*(x - cx)/(side/2), 2);
1115
                        double yterm = Math.pow(1.0*(y - cy)/(side/2), 2);
1116
                        return (xterm + yterm <= 1);
1117
                }
1118
                
1119
                public void notifyMouseEvent (MouseEvent e, boolean send) {
1120
                        if (!isValidPoint(e.getX(), e.getY()))
1121
                                return;
1122
                        vectorController.setPoint(e.getX(), e.getY());
1123
                        vectorController.repaint();
1124
                        if (send)
1125
                                vectorController.sendToServer();
1126
                }
1127
                
1128
                public void mouseExited(MouseEvent e) {
1129
                }
1130
                public void mouseEntered(MouseEvent e) {
1131
                }
1132
                public void mouseReleased(MouseEvent e) {
1133
                        this.notifyMouseEvent(e, true);
1134
                }
1135
                public void mouseClicked(MouseEvent e) {
1136
                        this.notifyMouseEvent(e, false);
1137
                }
1138
                public void mousePressed(MouseEvent e) {
1139
                }
1140
                public void mouseDragged(MouseEvent e) {
1141
                        vectorController.notifyMouseEvent(e, false);
1142
                }
1143
                public void mouseMoved(MouseEvent e) {
1144
                }
1145
                
1146
                public int getSpeed () {
1147
                        int dx = x - cx;
1148
                        int dy = y - cy;
1149
                        int v = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) );
1150
                        return v;
1151
                }
1152
                
1153
                /** 
1154
                * Returns the angle of the control vector in positive degrees west of north,
1155
                * or negative degrees east of north, whichever is less than or equal to
1156
                * 180 degrees total.
1157
                */
1158
                public int getAngle () {
1159
                        int dx = x - cx;
1160
                        int dy = cy - y;
1161
                        // find reference angle in radians
1162
                        double theta = Math.atan2(Math.abs(dx), Math.abs(dy));
1163
                        // transform to degrees
1164
                        theta = theta * 180 / Math.PI;
1165
                        // adjust for quadrant
1166
                        if (dx < 0 && dy < 0)
1167
                                theta = 90 + theta;
1168
                        else if (dx < 0 && dy >= 0)
1169
                                theta = 90 - theta;
1170
                        else if (dx >= 0 && dy < 0)
1171
                                theta = -90 - theta;
1172
                        else
1173
                                theta = -90 + theta;
1174
                        return (int) theta;
1175
                }
1176
                
1177
                public void paint (Graphics g) {
1178
                        g.setColor(Color.BLACK);
1179
                        g.fillRect(0, 0, width, height);
1180
                        ((Graphics2D)g).setStroke(new BasicStroke(1));
1181
                        g.setColor(Color.RED);
1182
                        g.drawOval(cx-side/2, cy-side/2, side, side);
1183
                        ((Graphics2D)g).setStroke(new BasicStroke(2));
1184
                        g.setColor(Color.GREEN);
1185
                        g.drawLine(cx, cy, x, y);
1186
                        g.fillOval(x-3, y-3, 6, 6);
1187
                }
1188
                
1189
                public void setMaxForward () {
1190
                        setPoint(cx, cy - (side/2) + 1);
1191
                }
1192
                
1193
                public void setMaxReverse () {
1194
                        setPoint(cx, cy + (side/2) - 1);
1195
                }
1196
                
1197
                public void setMaxLeft () {
1198
                        setPoint(cx - (side/2) + 1, cy);
1199
                }
1200
                
1201
                public void setMaxRight () {
1202
                        setPoint(cx + (side/2) - 1, cy);
1203
                }
1204
                
1205
                public void setZero () {
1206
                        setPoint(cx, cy);
1207
                }
1208
                
1209
                public void sendToServer () {
1210
                        System.out.println("Attempting to send angle = " + getAngle() + ", speed = " + getSpeed() + "");
1211
                        String dest = ColonetServerInterface.GLOBAL_DEST;
1212
                        if (cmbRobotNum != null && cmbRobotNum.getSelectedIndex() > 0) {
1213
                                dest = (String)cmbRobotNum.getSelectedItem();
1214
                        }
1215
                        
1216
                        if (csi != null) {
1217
                                /*
1218
                                csi.sendData(ColonetServerInterface.MOVE + " " + getSpeed() + " " + getAngle(), dest);
1219
                                */
1220
                                
1221
                                //Directional commands
1222
                                if (x > cx && y == cy) {  //move right
1223
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", dest);
1224
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", dest);
1225
                                } else if (x < cx && y == cy) {  //move left
1226
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", dest);
1227
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", dest);
1228
                                } else if (x == cx && y > cy) {  //move forward
1229
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 225", dest);
1230
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 225", dest);
1231
                                } else if (x == cx && y < cy) {  //move backward
1232
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 225", dest);
1233
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 225", dest);
1234
                                } else if (x == cx && y == cy) {  //stop!
1235
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 0", dest);
1236
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 0", dest);
1237
                                }
1238
                        }
1239
                }
1240
        
1241
        }
1242
        
1243
        /*
1244
        *        TaskAddWindow class
1245
        *        A window that provides a simple way to add tasks to a task queue.
1246
        */
1247
        class TaskAddWindow extends JFrame implements ActionListener, ListSelectionListener {
1248
                JPanel panelButtons;
1249
                JPanel panelParameters;
1250
                JPanel panelSouth;
1251
                JPanel panelSelection;
1252
                JButton btnSubmit;
1253
                JButton btnCancel;
1254
                DefaultListModel availableListModel;
1255
                JList availableList;
1256
                JScrollPane spAvailableTasks;
1257
                JTextArea txtDescription;
1258
                JTextField txtParameters;
1259
                
1260
                public TaskAddWindow () {
1261
                        super("Add a Task");
1262
                        super.setSize(500,500);
1263
                        super.setLayout(new BorderLayout());
1264
                        
1265
                        // set up buttons
1266
                        btnSubmit = new JButton("Submit");
1267
                        btnCancel = new JButton("Cancel");
1268
                        panelButtons = new JPanel();
1269
                        panelButtons.setLayout(new FlowLayout());
1270
                        panelButtons.add(btnSubmit);
1271
                        panelButtons.add(btnCancel);
1272
                        this.getRootPane().setDefaultButton(btnSubmit);
1273
                        
1274
                        // set up task list
1275
                        availableListModel = new DefaultListModel();
1276
                        availableListModel.addElement("Map the Environment");
1277
                        availableListModel.addElement("Clean Up Chemical Spill");
1278
                        availableListModel.addElement("Grow Plants");
1279
                        availableListModel.addElement("Save the Cheerleader");
1280
                        availableListModel.addElement("Save the World");
1281
                        availableList = new JList(availableListModel);
1282
                        availableList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1283
                        availableList.setSelectedIndex(-1);
1284
                        spAvailableTasks = new JScrollPane(availableList);
1285
                        spAvailableTasks.setBorder(BorderFactory.createTitledBorder("Select A Task"));
1286
                        txtDescription = new JTextArea();
1287
                        txtDescription.setEditable(false);
1288
                        txtDescription.setLineWrap(true);
1289
                        txtDescription.setWrapStyleWord(true);
1290
                        txtDescription.setBorder(BorderFactory.createTitledBorder("Description"));
1291
                        
1292
                        //set up parameter area
1293
                        panelParameters = new JPanel();
1294
                        panelParameters.setLayout(new BorderLayout());
1295
                        txtParameters = new JTextField();
1296
                        panelParameters.add(new JLabel("Optional parameters for this task: "), BorderLayout.WEST);
1297
                        panelParameters.add(txtParameters);
1298
                        
1299
                        // assemble objects
1300
                        panelSelection = new JPanel();
1301
                        panelSelection.setLayout(new GridLayout(1,2));
1302
                        panelSelection.add(spAvailableTasks);
1303
                        panelSelection.add(txtDescription);
1304
                        
1305
                        panelSouth = new JPanel();
1306
                        panelSouth.setLayout(new GridLayout(2,1));
1307
                        panelSouth.add(panelParameters);
1308
                        panelSouth.add(panelButtons);
1309
                        
1310
                        this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
1311
                        this.getContentPane().add(panelSelection, BorderLayout.CENTER);
1312
                        this.setLocationRelativeTo(null);
1313
                        
1314
                        // add listeners here
1315
                        availableList.addListSelectionListener(this);
1316
                        btnSubmit.addActionListener(this);
1317
                        btnCancel.addActionListener(this);
1318
                }
1319
                
1320
                public void prompt () {
1321
                        this.setVisible(true);
1322
                }
1323
                
1324
                private String getDescription (int index) {
1325
                        if (index < 0)
1326
                                return "";
1327
                        switch (index) {
1328
                                case 0: return "SLAM and junk";
1329
                                case 1: return "I'm not sure this works";
1330
                                case 2: return "Push them into the light";
1331
                                case 3: return "...";
1332
                                case 4: return "...";
1333
                        
1334
                                default: return "Task not recognized";
1335
                        }
1336
                }
1337
                
1338
                public void actionPerformed (ActionEvent e) {
1339
                        Object source = e.getSource();
1340
                        if (source == btnSubmit) {
1341
                                txtParameters.setText(txtParameters.getText().trim());
1342
                                
1343
                                
1344
                                this.setVisible(false);
1345
                        } else if (source == btnCancel) {
1346
                                this.setVisible(false);
1347
                        }
1348
                }
1349
                
1350
                public void valueChanged (ListSelectionEvent e) {
1351
                        int index = availableList.getSelectedIndex();
1352
                        if (index >= 0)
1353
                                txtDescription.setText(getDescription(index));
1354
                }
1355
        
1356
        }
1357
        
1358
        /*
1359
        *  BatteryIcon class
1360
        *  Graphical representation of battery level
1361
        */
1362
        class BatteryIcon implements Icon {
1363
                private int width;
1364
            private int height;
1365
            private int level;
1366
            
1367
                /** 
1368
                * Constructs a new BatteryIcon with all default parameters.
1369
                * Default width and height are 50.
1370
                * Default level is 100.
1371
                */
1372
            public BatteryIcon(){
1373
                    this(100, 50, 50);
1374
            }
1375
            
1376
                /** 
1377
                * Constructs a new BatteryIcon with default width and height, and with the specified level.
1378
                * Default width and height are 50.
1379
                */
1380
            public BatteryIcon(int startLevel){
1381
                    this(startLevel, 50, 50);
1382
            }
1383
            
1384
                /** 
1385
                * Constructs a new BatteryIcon with the specified level, width, and height.
1386
                */
1387
            public BatteryIcon(int startLevel, int w, int h){
1388
                    level = startLevel;
1389
                    width = w;
1390
                    height = h;
1391
            }
1392
            
1393
            public void paintIcon(Component c, Graphics g, int x, int y) {
1394
                Graphics2D g2d = (Graphics2D) g.create();
1395
                //clear the background
1396
                g2d.setColor(Color.WHITE);
1397
                g2d.fillRect(x + 1, y + 1, width - 2, height - 2);
1398
                //outline
1399
                g2d.setColor(Color.BLACK);
1400
                g2d.drawRect((int)(x + width*.3), y + 2, (int)(width*.4), height - 4);
1401
                //battery life rectangle
1402
                        if (level > 50)
1403
                                g2d.setColor(Color.GREEN);
1404
                        else if (level > 25)
1405
                                g2d.setColor(Color.YELLOW);
1406
                        else
1407
                                g2d.setColor(Color.RED);
1408
                int greenX = (int)(x + 1 + width*.3);
1409
                int greenY = (int)((y+3) + Math.abs(level-100.0)*(height-6)/(100));
1410
                int greenWidth = (int)(width*.4 - 2)+1;
1411
                int greenHeight = 1+(int)(level-0.0)*(height-6)/(100);
1412
                g2d.fillRect(greenX, greenY, greenWidth, greenHeight);
1413
                //text
1414
                g2d.setColor(Color.BLACK);
1415
                g2d.drawString(level + "%", greenX + greenWidth/2 - 10, greenY + greenHeight/2 + 5);
1416
                
1417
                g2d.dispose();
1418
            }
1419
            
1420
                /**
1421
                * Sets the battery level for this BatteryIcon. The level should be given in raw form, i.e. 0-255 directly 
1422
                * from the robot. The value will be converted to a representative percentage automatically.
1423
                *
1424
                * @param newLevel the new battery reading from the robot that this BatteryIcon will display.
1425
                */
1426
            public void setLevel(int newLevel) {
1427
                    level = convert(newLevel);
1428
                    repaint();
1429
                    System.out.println("Updated level to " + level);
1430
            }
1431
                
1432
            public int getIconWidth() {
1433
                return width;
1434
            }
1435
            
1436
            public int getIconHeight() {
1437
                return height;
1438
            }
1439
                
1440
                /**
1441
                * Converts a robot battery reading into representable form.
1442
                * Readings from the robot are returned as raw values, 0-255. This method converts the reading into a value
1443
                * from 0 to 100 so that the practical remaining charge is represented.
1444
                *
1445
                * @param level The battery level as returned by the robot.
1446
                * @returns The representable battery percentage.
1447
                */
1448
                private int convert (int level) {
1449
                        // TODO: make this a forreals conversion.
1450
                        return (int) (100.0 * level / 160);
1451
                }
1452
                
1453
        }
1454

    
1455

    
1456

    
1457
}