Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / colonet / ColonetGUI / Colonet.java @ 427

History | View | Annotate | Download (42.5 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
        BatteryIcon batteryIcon;
65
        JPanel panelBattery;
66
        VectorController vectorController;
67
        BufferedImage imageVectorControl;
68
        JButton btnAssignID;
69
        JButton btnCommand_StopTask;
70
        JButton btnCommand_ResumeTask;
71
        JButton btnCommand_ChargeNow;
72
        JButton btnCommand_StopCharging;
73
        
74
        // Task Manager
75
        JPanel panelTaskManager;
76
        JScrollPane spTaskManager;
77
        JPanel panelTaskManagerControls;
78
        JPanel panelTaskManagerControlsPriority;
79
        DefaultListModel taskListModel;
80
        JList taskList;
81
        JButton btnAddTask;
82
        JButton btnRemoveTask;
83
        JButton btnMoveTaskUp;
84
        JButton btnMoveTaskDown;
85
        JButton btnUpdateTasks;
86
        TaskAddWindow taskAddWindow;
87
        
88
        //Webcam
89
        WebcamPanel panelWebcam;
90
        GraphicsPanel panelGraph;
91
        GraphicsConfiguration gc;
92
        volatile BufferedImage image;
93
        volatile Graphics2D canvas;
94
        int cx, cy;
95
        JTabbedPane tabPaneMain;
96
        
97
        Font botFont;
98
        volatile int numBots;
99
        volatile int selectedBot;  //the user has selected this bot graphically
100
        volatile java.util.List <RobotIcon> robotIcons;  //contains boundary shapes around bots for click detection
101
        volatile int[] xbeeID;
102
        
103
        Thread drawThread;
104
        SelectionIndicator indicator;
105
        WebcamLoader webcamLoader;
106
        ColonetServerInterface csi;
107

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

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

    
294
                // Put all elements in the ContentPane
295
                this.getContentPane().setLayout(new BorderLayout());
296
                this.getContentPane().add(tabPaneMain, BorderLayout.CENTER);
297
                this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
298
                this.getContentPane().add(panelControl, BorderLayout.EAST);
299
                this.setVisible(true);
300
                
301
                /* Add all listeners here */
302
                // Task Management
303
                btnAddTask.addActionListener(this);
304
                btnRemoveTask.addActionListener(this);
305
                btnMoveTaskUp.addActionListener(this);
306
                btnMoveTaskDown.addActionListener(this);
307
                btnUpdateTasks.addActionListener(this);
308
                // Robot Control
309
                btnF.addActionListener(this);
310
                btnB.addActionListener(this);
311
                btnL.addActionListener(this);
312
                btnR.addActionListener(this);
313
                btnF.addKeyListener(this);
314
                btnB.addKeyListener(this);
315
                btnL.addKeyListener(this);
316
                btnR.addKeyListener(this);
317
                btnActivate.addActionListener(this);
318
                btnActivate.addKeyListener(this);
319
                cmbRobotNum.addKeyListener(this);
320
                btnCommand_StopTask.addActionListener(this);
321
                btnCommand_ResumeTask.addActionListener(this);
322
                btnCommand_ChargeNow.addActionListener(this);
323
                btnCommand_StopCharging.addActionListener(this);
324
                // Other
325
                btnConnect.addActionListener(this);
326
                btnGetXBeeIDs.addActionListener(this);
327
                btnAssignID.addActionListener(this);
328
                panelWebcam.addMouseListener(this);        
329
                                
330
                // Set up animation threads
331
                indicator = new SelectionIndicator(canvas);
332
                indicator.setRadius(RADIUS+3, 15);  //a tad more than the bot radius
333
        
334
        }
335
        
336
        public synchronized void paint (Graphics g) {
337
                /*        Redraw the graphical components in the applet. 
338
                        This paint method overrides the built-in paint of the 
339
                        JApplet, and we don't want to deal with redrawing the
340
                        components manually. Fuck that shit. */
341
                super.paint(g);
342
        }
343
        
344
        public synchronized void update (Graphics g) {
345
                paint(g);
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
        /** 
360
        * Gets the JTextArea used for storing the activity log. This method returns a reference to the 
361
        * JTextArea that stores the log. The log can contain any activity that is revelant to the use
362
        * of the applet, and may optionally display debugging information.
363
        *
364
        * @return the JTextArea where BOM matrix information is stored.
365
        */ 
366
        public JTextArea getLog () {
367
                return log;
368
        }
369
        
370
        /** 
371
        * Gets the JTextArea used for storing the BOM matrix data. This method returns a reference to the 
372
        * JTextArea that stores the BOM matrix. The values in the matrix are stored as integers separated 
373
        * by spaces, and the lines should be separated by a newline.
374
        * 
375
        * @return the JTextArea where BOM matrix information is stored.
376
        */
377
        public JTextArea getMatrixInput () {
378
                return txtMatrix;
379
        }
380
        
381
        /**
382
        * Parses a String containing BOM matrix information.
383
        * The ColonetServerInterface receives lines of the BOM matrix.  (For encoding 
384
        * information, see the ColonetServerInterface documentation.)  The entire matrix is passed
385
        * to the client when requested. This method takes a string of the form 
386
        * "[command code] [command code] [number of robots] [data0] [data1] ..."
387
        * with tokens separated by spaces and containing no brackets.  
388
        * The [command code]s are predefined values identifying this String as a BOM data
389
        * String, [number of robots] is an integer, and the values that follow are 
390
        * the sensor readings of the robots in order, starting with robot 0.  Only [number of robots]^2
391
        * data entries will be read.  The matrix values are saved locally until the next String is parsed.
392
        * 
393
        *
394
        * @param line the String containing BOM matrix information.
395
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots]^2 data entries in the String
396
        */
397
        public void parseMatrix (String line) {
398
                txtMatrix.setText("");
399
                String [] str = line.split(" ");
400
                int num = Integer.parseInt(str[2]);
401
                for (int i = 0; i < num; i++) {
402
                        for (int j = 0; j < num; j++) {
403
                                String next = str[3 + i*num + j];
404
                                if (next.equals("-1"))
405
                                        txtMatrix.append("-");
406
                                else 
407
                                        txtMatrix.append(next);
408
                                if (j < num - 1) 
409
                                        txtMatrix.append(" ");
410
                        }
411
                        if (i < num - 1) 
412
                                txtMatrix.append("\n");
413
                }
414
                
415
        }
416
        
417
        public void connect () {
418
            webcamLoader = new WebcamLoader(this);
419
                dataUpdater = new DataUpdater();
420
                csi = new ColonetServerInterface(this);
421
                csi.connect(txtHost.getText(), txtPort.getText());
422
                if (!csi.isReady())
423
                        return;
424
                btnConnect.setEnabled(false);
425
                lblConnectionStatus.setText("Status: Connected");
426
                dataUpdater.start();
427
                webcamLoader.start();
428
        }
429
        
430
        public void disconnect () {
431
            btnConnect.setEnabled(true);
432
            lblConnectionStatus.setText("Status: Disconnected");
433
            try { drawThread.interrupt(); } catch (Exception e) { }
434
                try { indicator.interrupt(); } catch (Exception e) { }
435
                
436
        }
437
        
438
        /**
439
        * Parses a String containing a task queue update.
440
        * Format is currently not specified.
441
        * This method currently does nothing.
442
        *
443
        * @param line the String containing task queue update information.
444
        */
445
        public void parseQueue (String line) {
446
                log.append("Got queue update\n");
447
                //TODO: display new queue data in tasks tab
448
        }
449
        
450
        /**
451
        * Parses a String containing XBee ID values.
452
        * The ColonetServerInterface receives Strings of XBee information.  (For encoding 
453
        * information, see the ColonetServerInterface documentation.)  This method takes
454
        * a string of the form "[command code] [command code] [number of robots] [id0] [id1] ..."
455
        * with tokens separated by spaces and containing no brackets.  
456
        * The [command code]s are predefined values identifying this String as an XBee
457
        * ID String, [number of robots] is an integer, and the values that follow are 
458
        * the IDs of the robots in order, starting with robot 0.  Only [number of robots] 
459
        * will be read.  The ID values are saved locally until the next String is parsed.
460
        * The purpose of having this list is to ensure that robots are properly identified for control purposes.
461
        * This keeps robot identification consistent between sessions and prevents arbitrary assignment. 
462
        *
463
        * @param line the String containing XBee ID information.
464
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots] IDs in the String
465
        * @see ColonetServerInterface#sendXBeeIDRequest()
466
        */
467
        public void parseXBeeIDs (String line) {
468
        
469
                String [] str = line.split(" ");
470
                int num = Integer.parseInt(str[2]);
471
                xbeeID = new int[num];
472
                for (int i = 0; i < num; i++)
473
                        xbeeID[i] = Integer.parseInt(str[i+3]);
474
                
475
                //update the list of robots to control
476
                //but save the old value first
477
                Object oldSelection = cmbRobotNum.getSelectedItem();
478
                cmbRobotNum.removeAllItems();
479
                cmbRobotNum.addItem(new String("   All   "));
480
                for (int i = 0; i < num; i++)
481
                        cmbRobotNum.addItem(new String("" + xbeeID[i]));
482
                cmbRobotNum.setSelectedItem(oldSelection);
483
        }
484
        
485
        /**
486
        * Parses a String containing battery information.
487
        * The ColonetServerInterface receives Strings of battery information.  (For encoding 
488
        * information, see the ColonetServerInterface documentation.)  This method takes
489
        * a string of the form "[command code] [command code] [robot ID] [value]"
490
        * with tokens separated by spaces and containing no brackets.  
491
        * The [command code]s are predefined values identifying this String as a battery
492
        * information String, [robot ID] is an integer, and [value] is a battery measurement.
493
        * This updates the batery information for a single robot.
494
        * 
495
        *
496
        * @param line the String containing battery information.
497
        * @see ColonetServerInterface#sendBatteryRequest(int)
498
        */
499
        public void parseBattery (String line) {
500
                String [] str = line.split(" ");
501
                int botNum = Integer.parseInt(str[2]);
502
                int level = Integer.parseInt(str[3]);
503
                int selectedBot = 0;
504
                try { 
505
                    selectedBot = Integer.parseInt((String)cmbRobotNum.getSelectedItem());
506
                } catch (Exception e) {
507
                }
508
                if (selectedBot == botNum) {
509
                        batteryIcon.setLevel(level);
510
                }
511
        }
512
        
513
        //
514
        // MouseListener methods
515
        //
516
        public void mouseExited(MouseEvent e) {
517
        }
518
        public void mouseEntered(MouseEvent e) {
519
        }
520
        public void mouseReleased(MouseEvent e) {
521
        }
522
        public void mouseClicked(MouseEvent e) {
523
        }
524
        public void mousePressed(MouseEvent e) {
525
                System.out.println(e);
526
        }
527
        public void mouseDragged(MouseEvent e) {
528
        }
529
        public void mouseMoved(MouseEvent e) {
530
        }
531
        
532
        //
533
        // KeyListener methods
534
        //
535
        public void keyPressed (KeyEvent e) {
536
                int code = e.getKeyCode();
537
                if (code == KeyEvent.VK_UP) {
538
                        vectorController.setMaxForward();
539
                        vectorController.sendToServer();
540
                } else if (code == KeyEvent.VK_DOWN) {
541
                        vectorController.setMaxReverse();
542
                        vectorController.sendToServer();
543
                } else if (code == KeyEvent.VK_LEFT) {
544
                        vectorController.setMaxLeft();
545
                        vectorController.sendToServer();
546
                } else if (code == KeyEvent.VK_RIGHT) {
547
                        vectorController.setMaxRight();
548
                        vectorController.sendToServer();
549
                } else if (code == KeyEvent.VK_S) {
550
                        vectorController.setZero();
551
                        vectorController.sendToServer();
552
                }
553
        }
554
        public void keyReleased (KeyEvent e) {
555
        }
556
        public void keyTyped (KeyEvent e) {
557
        }
558
        
559
        
560
        //
561
        // ActionListener method
562
        //
563
        public void actionPerformed (ActionEvent e) {
564
                Object source = e.getSource();
565
                
566
                // General Actions
567
                if (source == btnConnect) {
568
                        connect();
569
                } else if (source == btnGetXBeeIDs) {
570
                        csi.sendXBeeIDRequest();
571
                } else if (source == btnAssignID) {
572
                    String message;
573
                        
574
                }
575
                
576
                // Robot Movement Controls
577
                else if (source == btnF) {
578
                        vectorController.setMaxForward();
579
                        vectorController.sendToServer();
580
                } else if (source == btnB) {
581
                        vectorController.setMaxReverse();
582
                        vectorController.sendToServer();
583
                } else if (source == btnL) {
584
                        vectorController.setMaxLeft();
585
                        vectorController.sendToServer();
586
                } else if (source == btnR) {
587
                        vectorController.setMaxRight();
588
                        vectorController.sendToServer();
589
                } else if (source == btnActivate) {
590
                        vectorController.setZero();
591
                        vectorController.sendToServer();
592
                }
593
                // Robot Commands (non-movement)
594
                else if (source == btnCommand_StopTask) {
595
                
596
                } else if (source == btnCommand_ResumeTask) {
597
                
598
                } else if (source == btnCommand_ChargeNow) {
599
                
600
                } else if (source == btnCommand_StopCharging) {
601
                
602
                }
603
                        
604
                // Queue Management
605
                else if (source == btnAddTask) {
606
                        taskAddWindow.prompt();
607
                } else if (source == btnRemoveTask) {
608
                        if (taskList.getSelectedIndex() >= 0);
609
                                csi.sendQueueRemove(taskList.getSelectedIndex());
610
                        csi.sendQueueUpdate();
611
                } else if (source == btnMoveTaskUp) {
612
                        csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
613
                        csi.sendQueueUpdate();
614
                } else if (source == btnMoveTaskDown) {
615
                        csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
616
                        csi.sendQueueUpdate();
617
                } else if (source == btnUpdateTasks) {
618
                        csi.sendQueueUpdate();
619
                }
620
        }
621
        
622
        /*
623
        *        SelectionIndicator thread.
624
        *        Graphical representation of the selection marker
625
        *
626
        *        step() and draw() are synchronized methods. step() is private and 
627
        *        used to update the position of the crosshairs. draw() is called 
628
        *        externally and should only run if all calculations in step() have
629
        *        been completed.
630
        */
631
        private class SelectionIndicator extends Thread {
632
        
633
                final int INDICATOR_DELAY = 180;
634
                final double DTHETA = 0.4;    //larger values make the marker rotate faster
635
                Graphics2D g;   //canvas to draw on
636
                boolean running;
637
                
638
                int sx, sy;                //center
639
                int r, dr;                //radius and width of marker
640
                double theta;   //current angle
641
                
642
                volatile Polygon poly1, poly2, poly3, poly4;
643
                
644
                int px1, py1;
645
                int rx1, ry1;
646
                int px2, py2;
647
                int rx2, ry2;
648
                int px3, py3;
649
                int rx3, ry3;
650
                int px4, py4;
651
                int rx4, ry4;
652
                
653
                int steps;
654
        
655
                public SelectionIndicator (Graphics2D g) {
656
                        super("SelectionIndicator");
657
                        this.g = g;
658
                        running = false;
659
                        steps = 0;
660
                        
661
                        theta = 0;
662
                        rx1 = 0; ry1 = 0;
663
                        px1 = 0; py1 = 0;
664
                        rx2 = 0; ry2 = 0;
665
                        px2 = 0; py2 = 0;
666
                        rx3 = 0; ry3 = 0;
667
                        px3 = 0; py3 = 0;
668
                        rx4 = 0; ry4 = 0;
669
                        px4 = 0; py4 = 0;
670
                }
671
                
672
                public synchronized void setCenter (int sx, int sy) {
673
                        if (sx == this.sx && sy == this.sy) return;
674
                        this.sx = sx;
675
                        this.sy = sy;
676
                        steps = 0;
677
                }
678
                
679
                public synchronized void setRadius (int r, int dr) {
680
                        this.r = r;
681
                        this.dr = dr;
682
                        steps = 0;
683
                }
684
                
685
                public void run () {
686
                        running = true;
687
                        while (running) {
688
                                step();
689
                                try { 
690
                                        Thread.sleep(INDICATOR_DELAY);
691
                                } catch (InterruptedException e) {
692
                                        running = false;
693
                                        return;
694
                                }
695
                        }
696
                }
697
                
698
                private synchronized void step () {
699
                        Polygon poly1_new = new Polygon();
700
                        Polygon poly2_new = new Polygon();
701
                        Polygon poly3_new = new Polygon();
702
                        Polygon poly4_new = new Polygon();
703
                
704
                        //the step
705
                        theta = (theta + DTHETA/Math.PI) % (Math.PI);
706
                        
707
                        //the calculation
708
                        //let p be the point of the pointy thing toward the center
709
                        //let r be the point at the opposite side
710
                        
711
                        //recalculate radius, if it will look cool, lolz
712
                        int newr = r;
713
                        if (steps < 100)
714
                        newr = (int)( r + 200/(steps+1) );
715
                        
716
                        //precompute values for dx and dy
717
                        int dx_inner = (int)(newr * Math.cos(theta));
718
                        int dy_inner = (int)(newr * Math.sin(theta));
719
                        int dx_outer = (int)((newr+dr) * Math.cos(theta));
720
                        int dy_outer = (int)((newr+dr) * Math.sin(theta));
721
                        
722
                        //calculate polygon constants
723
                        int dy_poly = (int)(dr/2 * Math.cos(theta));
724
                        int dx_poly = (int)(dr/2 * Math.sin(theta));
725
                        
726
                        //determine critical points
727
                        //kansas city shuffle!
728
                        px1 = sx + dx_inner;
729
                        py1 = sy - dy_inner;
730
                        rx1 = sx + dx_outer;
731
                        ry1 = sy - dy_outer;
732
                        px2 = sx - dx_inner;
733
                        py2 = sy + dy_inner;
734
                        rx2 = sx - dx_outer;
735
                        ry2 = sy + dy_outer;
736
                        px3 = sx - dy_inner;
737
                        py3 = sy - dx_inner;
738
                        rx3 = sx - dy_outer;
739
                        ry3 = sy - dx_outer;
740
                        px4 = sx + dy_inner;
741
                        py4 = sy + dx_inner;
742
                        rx4 = sx + dy_outer;
743
                        ry4 = sy + dx_outer;
744
                        
745
                        //create polygons
746
                        poly1_new.addPoint(px1, py1);
747
                        poly1_new.addPoint(rx1+dx_poly, ry1+dy_poly);
748
                        poly1_new.addPoint(rx1-dx_poly, ry1-dy_poly);
749
                        poly2_new.addPoint(px2, py2);
750
                        poly2_new.addPoint(rx2+dx_poly, ry2+dy_poly);
751
                        poly2_new.addPoint(rx2-dx_poly, ry2-dy_poly);
752
                        poly3_new.addPoint(px3, py3);
753
                        poly3_new.addPoint(rx3-dy_poly, ry3+dx_poly);
754
                        poly3_new.addPoint(rx3+dy_poly, ry3-dx_poly);
755
                        poly4_new.addPoint(px4, py4);
756
                        poly4_new.addPoint(rx4-dy_poly, ry4+dx_poly);
757
                        poly4_new.addPoint(rx4+dy_poly, ry4-dx_poly);
758
                        
759
                        //reassign updated polygons
760
                        poly1 = poly1_new;
761
                        poly2 = poly2_new;
762
                        poly3 = poly3_new;
763
                        poly4 = poly4_new;
764
                
765
                        if (steps < 300) steps++;
766
                }
767
                
768
                public synchronized void draw () {
769
                        if (!running) return;
770
                        g.setColor(Color.GRAY);
771
                        //draw polygons
772
                        g.fillPolygon(poly1);
773
                        g.fillPolygon(poly2);
774
                        g.fillPolygon(poly3);
775
                        g.fillPolygon(poly4);
776
                }
777
        
778
        }
779
        
780
        /*
781
        *        DataUpdater thread.
782
        *   The purpose of this thread is to request data from the server at regular intervals.
783
        *
784
        */
785
        class DataUpdater extends Thread {
786
                final int DATAUPDATER_DELAY = 2200;
787
                
788
                public DataUpdater () {
789
                        super("Colonet DataUpdater");
790
                }
791
                
792
                public void run () {
793
                        String line;
794
                        while (true) {
795
                                try {
796
                                        //request more data
797
                                        if (csi != null && csi.isReady()) {
798
                                                //csi.sendSensorDataRequest();
799
                                                csi.sendXBeeIDRequest();
800
                                                if (cmbRobotNum.getSelectedIndex() > 0) {
801
                                                    String sel = (String) cmbRobotNum.getSelectedItem();
802
                                                    int num = Integer.parseInt(sel);
803
                                                        csi.sendBatteryRequest(num);
804
                                                } else {
805
                                                        csi.sendBatteryRequest(200);
806
                                                }
807
                                        }
808
                                        Thread.sleep(DATAUPDATER_DELAY);
809
                                } catch (InterruptedException e) {
810
                                        return;
811
                                } 
812
                        }
813
                }
814

    
815
        }
816
        
817
        /*
818
        *        GraphicsPanel class
819
        *        An extension of JPanel, designed for holding an image that will be repainted regularly.
820
        */
821
        class GraphicsPanel extends JPanel {
822
                protected Image img;
823
        
824
                public GraphicsPanel (Image img) {
825
                        super();
826
                        this.img = img;
827
                }
828
                
829
                public GraphicsPanel (Image img, boolean isDoubleBuffered) {
830
                        super(isDoubleBuffered);
831
                        this.img = img;
832
                }
833
                
834
                public void paint (Graphics g) {
835
                        // Place the buffered image on the screen, inside the panel
836
                        g.drawImage(img, 0, 0, Color.WHITE, this);
837
                }
838
        
839
        }
840
        
841
        /*
842
        *        WebcamPanel class
843
        *        Enables more efficient image handling in a component-controlled environment
844
        */
845
        class WebcamPanel extends JPanel {
846
                int BORDER = 16;  // this is arbitrary. it makes the image look nice inside a border.
847
                int BOT_RADIUS = 40;
848
                volatile BufferedImage img;
849
        
850
                public WebcamPanel () {
851
                        super();
852
                }
853
                
854
                public synchronized void setImage (BufferedImage newimg) {
855
                        if (img != null) {
856
                                img.flush();
857
                                img = null;
858
                                img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
859
                        }
860
                        img = newimg;
861
                }
862
                
863
                public synchronized void setPoints (Point [] newpoints) {
864
                        // test code -- need to remove later
865
                        if (newpoints == null) {
866
                                Point [] p = new Point [3];
867
                                p[0] = new Point(100,150);
868
                                p[1] = new Point(200,200);
869
                                p[2] = new Point(400,300);
870
                                setPoints(p);
871
                                return;
872
                        }
873
                        
874
                        for (int i = 0; i < newpoints.length; i++) {
875
                                Point p = newpoints[i];
876
                                boolean found = false;
877
                                for (int j = 0; j < robotIcons.size(); j++) {
878
                                        RobotIcon r = robotIcons.get(i);
879
                                        if (r.isClose(p.x, p.y)) {
880
                                                r.move(p.x, p.y);
881
                                                found = true;
882
                                        }
883
                                }
884
                                if (!found) {
885
                                        RobotIcon r = new RobotIcon(p.x, p.y);
886
                                        robotIcons.add(r);
887
                                        System.out.println("Added " + r);
888
                                }
889
                        }
890
                        
891
                }
892
                
893
                public synchronized void paint (Graphics g) {
894
                        
895
                        if (img == null)
896
                                return;
897
                        // Place the image on the screen, inside the panel
898
                        g.drawImage(img, 
899
                                                BORDER,        //dx1 - the x coordinate of the first corner of the destination rectangle.
900
                                                BORDER,        //dy1 - the y coordinate of the first corner of the destination rectangle.
901
                                                this.getWidth() - BORDER,         //dx2 - the x coordinate of the second corner of the destination rectangle.
902
                                                this.getHeight() - BORDER,        //dy2 - the y coordinate of the second corner of the destination rectangle.
903
                                                0,        //sx1 - the x coordinate of the first corner of the source rectangle.
904
                                                0,        //sy1 - the y coordinate of the first corner of the source rectangle.
905
                                                image.getWidth(),        //sx2 - the x coordinate of the second corner of the source rectangle.
906
                                                image.getHeight(),        //sy2 - the y coordinate of the second corner of the source rectangle.
907
                                                null        //observer - object to be notified as more of the image is scaled and converted.
908
                                                );
909
                                                
910
                        // Draw Identifiers
911
                        if (robotIcons == null)
912
                                return;
913
                        ((Graphics2D)g).setStroke(new BasicStroke(2));
914
                        for (int i = 0; i < robotIcons.size(); i++) {
915
                                RobotIcon r = robotIcons.get(i);
916
                                g.setColor(r.color);
917
                                g.drawOval(r.x, r.y, 2*r.RADIUS, 2*r.RADIUS);
918
                                System.out.println("Painted: " + r);
919
                        }
920
                        
921
                }
922
        
923
        }
924
        
925
        /*
926
        *        WebcamLoader class
927
        *        Handles the loading of the webcam image.
928
        */
929
        class WebcamLoader extends Thread 
930
        {
931
                final int WEBCAMLOADER_DELAY = 1000;
932
                final String IMAGE_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet.jpg";
933
                final String LOCATIONS_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet/locations.txt";
934
                
935
                URL imagePath;
936
                URI locationsPath;
937
                
938
                MediaTracker mt;
939
                BufferedImage image;
940
                
941
                public WebcamLoader (JApplet applet)
942
                {
943
                        super("ColonetWebcamLoader");
944
                        mt = new MediaTracker(applet);
945
                        ImageIO.setUseCache(false);
946
                        try {
947
                                imagePath = new URL(IMAGE_PATH);
948
                        } catch (MalformedURLException e) {
949
                                System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n");
950
                        }
951
                        try {
952
                                locationsPath = new URI(LOCATIONS_PATH);
953
                        } catch (URISyntaxException x) {
954
                                System.out.println("Malformed URI: could not form URI from: [" + LOCATIONS_PATH + "]\n");
955
                        }
956
                        
957
                }
958
                
959
                public synchronized void run ()
960
                {
961
                        while (true) {
962
                                try {
963
                                        Thread.sleep(WEBCAMLOADER_DELAY);
964
                                        if (image != null) 
965
                                                image.flush();
966
                                        image = ImageIO.read(imagePath);
967
                                        // The MediaTracker waitForID pauses the thread until the image is loaded.
968
                                        // We don't want to display a half-downloaded image.
969
                                        mt.addImage(image, 1);
970
                                        mt.waitForID(1);
971
                                        mt.removeImage(image);
972
                                        // Save
973
                                        panelWebcam.setImage(image);
974
                                        //parseLocations(locationsPath.toURL());
975
                                        panelWebcam.setPoints(null);  //temporary simulation code
976
                                } catch (InterruptedException e) {
977
                                        return;
978
                                } catch (java.security.AccessControlException e) {
979
                                        csi.warn("Could not load webcam.\n" + e);
980
                                        return;
981
                                } catch (IOException e) {
982
                                        log.append("IOException while trying to load image.");
983
                                }
984
                        }
985
                }
986
                
987
                private void parseLocations (URL url) {
988
                        URLConnection conn = null;
989
                        DataInputStream data = null;
990
                        String line;
991
                        String [] lines = new String[30];
992
                        StringBuffer buf = new StringBuffer();
993
                        int i = 0;
994
                
995
                        try {
996
                                conn = url.openConnection();
997
                                conn.connect();
998
                                data = new DataInputStream(new BufferedInputStream(conn.getInputStream()));
999
                                while ((line = data.readLine()) != null) {
1000
                                        buf.append(line + ";");
1001
                                        lines[i] = line;
1002
                                        i++;
1003
                                }
1004
                                data.close();
1005
                        } catch (IOException e) {
1006
                                System.out.println("IOException:" + e.getMessage());
1007
                        }
1008
                        //log.append("Got robot locations: " + buf + "\n");
1009
                        
1010
                        // Get Point values from strings
1011
                        Point [] points = new Point[i];
1012
                        for (int j = 0; j < i; j++) {
1013
                                String [] parts = lines[j].split(",");
1014
                                int xval = Integer.parseInt(parts[0]);
1015
                                int yval = Integer.parseInt(parts[1]);
1016
                                Point p = new Point(xval, yval);
1017
                                points[j] = p;
1018
                        }
1019
                        if (points.length != 0)
1020
                                panelWebcam.setPoints(points);
1021
                        
1022
                }
1023
        }
1024
        
1025
        /*
1026
        *  RobotIcon class
1027
        *  Provides a means for graphically representing and keeping track of webcam bots.
1028
        */
1029
        class RobotIcon {
1030
                public final int RADIUS = 30;
1031
                public final int CLOSE = 40;
1032
                
1033
                public int x, y;
1034
                public int id;
1035
                public Color color;
1036
                
1037
                public RobotIcon (int x, int y) {
1038
                        this.id = -1;
1039
                        this.color = Color.RED;
1040
                        this.x = x;
1041
                        this.y = y;
1042
                }
1043
                
1044
                public void move (int newX, int newY) {
1045
                        this.x = newX;
1046
                        this.y = newY;
1047
                }
1048
                
1049
                public boolean isClose (int nx, int ny) {
1050
                        int dist = (int) Point.distance(this.x, this.y, nx, ny);
1051
                        return (dist < CLOSE);
1052
                }
1053
                
1054
                public String toString () {
1055
                        String s = "RobotIcon at (" + x + "," + y + "), id " + id;
1056
                        return s;
1057
                }
1058
                
1059
        }
1060

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

    
1433

    
1434

    
1435
}