Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (43.9 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.Random;
14

    
15

    
16
/**
17
*        The Colonet Graphical User Interface Applet for use locally and over an internet connection.
18
*        @author Gregory Tress
19
*        
20
*
21
*/
22
public class Colonet extends JApplet implements ActionListener, MouseInputListener, KeyListener, Runnable {
23

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

    
28
        // Connection
29
        JTextField txtHost;                                
30
        JTextField txtPort;                                
31
        JButton btnConnect;        
32
        JButton btnGraph;
33
        JLabel lblConnectionStatus;
34
        JTextArea txtMatrix;
35
        JTextArea txtInfo; 
36
        JPanel panelConnect;
37
        JPanel panelServerInterface;
38
        
39
        // South
40
        JPanel panelSouth;
41
        JTextArea log;
42
        JScrollPane spLog;
43
        
44
        // Control
45
        JPanel panelControl;
46
        JTabbedPane tabPaneControl;
47
        JPanel panelRobotControl;
48
        JPanel panelRobotDirection;
49
        JPanel panelRobotDirectionButtons;
50
        JPanel panelRobotCommands;
51
        JButton btnF, btnB, btnL, btnR, btnActivate;
52
        JComboBox cmbRobotNum;
53
        JLabel lblBattery;
54
        BatteryIcon batteryIcon;
55
        JPanel panelBattery;
56
        VectorController vectorController;
57
        BufferedImage imageVectorControl;
58
        JButton btnCommand_StopTask;
59
        JButton btnCommand_ResumeTask;
60
        JButton btnCommand_ChargeNow;
61
        JButton btnCommand_StopCharging;
62
        
63
        // Task Manager
64
        JPanel panelTaskManager;
65
        JScrollPane spTaskManager;
66
        JPanel panelTaskManagerControls;
67
        JPanel panelTaskManagerControlsPriority;
68
        DefaultListModel taskListModel;
69
        JList taskList;
70
        JButton btnAddTask;
71
        JButton btnRemoveTask;
72
        JButton btnMoveTaskUp;
73
        JButton btnMoveTaskDown;
74
        JButton btnUpdateTasks;
75
        TaskAddWindow taskAddWindow;
76
        
77
        //Webcam and Graph
78
        WebcamPanel panelWebcam;
79
        GraphicsPanel panelGraph;
80
        GraphicsConfiguration gc;
81
        volatile BufferedImage image;
82
        volatile Graphics2D canvas;
83
        int cx, cy;
84
        JTabbedPane tabPaneMain;
85
        
86
        
87
        Socket socket;                                        
88
        OutputStreamWriter out;
89
        DataUpdater dataUpdater;  
90
        
91
        Font botFont;
92
        volatile int tokenLoc;  //the token is currently here
93
        volatile int numBots;
94
        volatile int selectedBot;  //the user has selected this bot
95
        volatile Rectangle[] botRect;  //contains boundary shapes around bots for click detection
96
        volatile int[] xbeeID;
97
        
98
        Thread drawThread;
99
        Simulator simulator;
100
        SelectionIndicator indicator;
101
        WebcamLoader webcamLoader;
102
        ColonetServerInterface csi;
103

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

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

    
287
                // Put all elements in the ContentPane
288
                this.getContentPane().setLayout(new BorderLayout());
289
                this.getContentPane().add(tabPaneMain, BorderLayout.CENTER);
290
                this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
291
                this.getContentPane().add(panelControl, BorderLayout.EAST);
292
                this.setVisible(true);
293
                
294
                /* Add all listeners here */
295
                // Task Management
296
                btnAddTask.addActionListener(this);
297
                btnRemoveTask.addActionListener(this);
298
                btnMoveTaskUp.addActionListener(this);
299
                btnMoveTaskDown.addActionListener(this);
300
                btnUpdateTasks.addActionListener(this);
301
                // Robot Control
302
                btnF.addActionListener(this);
303
                btnB.addActionListener(this);
304
                btnL.addActionListener(this);
305
                btnR.addActionListener(this);
306
                btnF.addKeyListener(this);
307
                btnB.addKeyListener(this);
308
                btnL.addKeyListener(this);
309
                btnR.addKeyListener(this);
310
                btnActivate.addActionListener(this);
311
                btnActivate.addKeyListener(this);
312
                cmbRobotNum.addKeyListener(this);
313
                vectorController.addMouseMotionListener(this);
314
                vectorController.addMouseListener(this);
315
                btnCommand_StopTask.addActionListener(this);
316
                btnCommand_ResumeTask.addActionListener(this);
317
                btnCommand_ChargeNow.addActionListener(this);
318
                btnCommand_StopCharging.addActionListener(this);
319
                // Other
320
                btnGraph.addActionListener(this);
321
                btnConnect.addActionListener(this);
322
                panelGraph.addMouseListener(this);
323
                this.addMouseMotionListener(this);        
324
                
325
                                
326
                // Set up dependent threads
327
                indicator = new SelectionIndicator(canvas);
328
                indicator.setRadius(RADIUS+3, 15);  //a tad more than the bot radius
329
                simulator = new Simulator();
330
                webcamLoader = new WebcamLoader(this);
331
                dataUpdater = new DataUpdater();
332
                csi = new ColonetServerInterface(this);
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
                step();
342
                super.paint(g);
343
        }
344
        
345
        public synchronized void update (Graphics g) {
346
                paint(g);
347
        }
348
        
349
        public void drawRobot (int id, int x, int y) {
350
                //save the bot in memory, so we can tell if we click on it later
351
                botRect[id] = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS);
352
        
353
                //draw the bot on the canvas
354
                canvas.setColor(Color.BLACK);
355
                canvas.drawOval(x-RADIUS, y-RADIUS, RADIUS*2, RADIUS*2);
356
                
357
                //draw the label
358
                canvas.setFont(botFont);
359
                try {
360
                        canvas.drawString("" + xbeeID[id], x-20, y+2);
361
                } catch (Exception e) {
362
                        canvas.drawString("???", x-22, y+2);
363
                }
364
        }
365
        
366
        public void drawConnection (int start, int end, int radius, Color color) {
367
                final int ARROW_LENGTH = 18;
368
        
369
                double angle = 2.0 * Math.PI / numBots;
370
                int startx, starty, endx, endy;
371
                startx = cx - (int)(radius * Math.cos(start * angle));
372
                starty = cy - (int)(radius * Math.sin(start * angle));
373
                endx = cx - (int)(radius * Math.cos(end * angle));
374
                endy = cy - (int)(radius * Math.sin(end * angle));
375
                canvas.setColor(color);
376
                canvas.drawLine(startx, starty, endx, endy);
377
                
378
                //create arrow
379
                if (color.equals(Color.BLACK)) return;
380
                int big_dy = starty - endy;
381
                int big_dx = endx - startx;
382
                double theta = 0;
383
                if (big_dx == 0 && starty > endy) //pointing up
384
                        theta = Math.PI/2;
385
                else if (big_dx == 0 && starty < endy) //pointing down 
386
                        theta = 3*Math.PI/2;
387
                else if (big_dy == 0 && startx > endx) //pointing left
388
                        theta = Math.PI;
389
                else if (big_dy == 0 && startx < endx) //pointing right
390
                        theta = 0;
391
                else
392
                        theta = Math.atan(1.0 * big_dy / big_dx);
393
                
394
                //create ploygon
395
                Polygon poly = new Polygon();
396
                int dx_arrow = Math.abs((int)(ARROW_LENGTH * Math.cos(theta)));
397
                int dy_arrow = Math.abs((int)(ARROW_LENGTH * Math.sin(theta)));
398
                int dy_half = (int)(ARROW_LENGTH/2 * Math.cos(theta));
399
                int dx_half = (int)(ARROW_LENGTH/2 * Math.sin(theta));
400
                int rx = (big_dx > 0) ? endx - dx_arrow : endx + dx_arrow;
401
                int ry = (big_dy > 0) ? endy + dy_arrow : endy - dy_arrow;
402
                poly.addPoint(endx, endy);
403
                poly.addPoint(rx - dx_half, ry - dy_half);
404
                poly.addPoint(rx + dx_half, ry + dy_half);
405
                canvas.fillPolygon(poly);
406
        }
407
        
408
        public void run () {
409
                while (true) {
410
                        repaint();
411
                        try { 
412
                                Thread.sleep(90);
413
                        } catch (InterruptedException e) {
414
                                return;
415
                        }
416
                }
417
        }
418
        
419
        private void step () {
420
                final int DIAMETER = image.getWidth() - 2*BUFFER;
421
                final int BIGRADIUS = DIAMETER / 2;
422
                final int TOKENRADIUS = 40;
423
                boolean valid;
424
        
425
                // clear image
426
                canvas.setColor(Color.WHITE);
427
                canvas.fillRect(0, 0, image.getWidth(), image.getHeight());
428
                
429
                // parse the matrix, to see what robots exist
430
                String [] rows = txtMatrix.getText().split("\n");
431
                numBots = rows.length;
432
                String [][] entries = new String[numBots][numBots];
433
                valid = true;
434
                for (int i = 0; i < numBots; i++) {
435
                        entries[i] = rows[i].split(" ");
436
                        if (entries[i].length != rows.length) valid = false;
437
                }
438
                
439
                if (valid) {
440
                        this.showStatus("Running");
441
                        
442
                        // draw robots and find which one is seleced
443
                        double angle = 2.0 * Math.PI / numBots;
444
                        canvas.setColor(Color.BLACK);
445
                        botRect = new Rectangle[numBots];
446
                        int x, y;
447
                        if (selectedBot >= numBots) selectedBot = 0;
448
                        for (int i = 0; i < numBots; i++) {
449
                                x = cx - (int)(BIGRADIUS * Math.cos(i * angle));
450
                                y = cy - (int)(BIGRADIUS * Math.sin(i * angle));
451
                                drawRobot(i, x, y);
452
                                if (i == selectedBot) indicator.setCenter(x, y);
453
                        }
454
                        
455
                        // draw token marker
456
                        int tokenx, tokeny;
457
                        int tokenNum = tokenLoc;
458
                        tokenx = cx - (int)(BIGRADIUS * Math.cos(tokenNum * angle));
459
                        tokeny = cy - (int)(BIGRADIUS * Math.sin(tokenNum * angle));
460
                        canvas.setColor(Color.RED);
461
                        canvas.drawOval(tokenx-TOKENRADIUS, tokeny-TOKENRADIUS, 2*TOKENRADIUS, 2*TOKENRADIUS);
462
                        
463
                        // create an inner circle along which the connections are made.
464
                        // let the diameter of this circle be 2*RADIUS less than the outerDiameter.
465
                        // see what connections exist
466
                        for (int row = 0; row < numBots; row++) {
467
                                for(int col = 0; col < numBots; col++) {
468
                                        if (!entries[row][col].equals("-") && entries[col][row].equals("-") && row != col) {
469
                                                //TODO: Make a standard gray
470
                                                drawConnection(row, col, BIGRADIUS-RADIUS, new Color(200,200,200));
471
                                        } else if (!entries[row][col].equals("-") && ! entries[col][row].equals("-") && row != col) {
472
                                                drawConnection(row, col, BIGRADIUS-RADIUS, Color.BLACK);
473
                                        }
474
                                }
475
                        }
476
                        
477
                        // draw the selection indicator
478
                        indicator.draw();
479
                        
480
                } else {  // if matrix is not valid
481
                        this.showStatus("Error: Invalid matrix");
482
                }
483
        
484
        }
485
        
486
        /** 
487
        * Gets the JTextArea used for storing the activity log. This method returns a reference to the 
488
        * JTextArea that stores the log. The log can contain any activity that is revelant to the use
489
        * of the applet, and may optionally display debugging information.
490
        *
491
        * @return the JTextArea where BOM matrix information is stored.
492
        */ 
493
        public JTextArea getLog () {
494
                return log;
495
        }
496
        
497
        /** 
498
        * Gets the JTextArea used for storing the BOM matrix data. This method returns a reference to the 
499
        * JTextArea that stores the BOM matrix. The values in the matrix are stored as integers separated 
500
        * by spaces, and the lines should be separated by a newline.
501
        * 
502
        * @return the JTextArea where BOM matrix information is stored.
503
        */
504
        public JTextArea getMatrixInput () {
505
                return txtMatrix;
506
        }
507
        
508
        /**
509
        * Parses a String containing BOM matrix information.
510
        * The ColonetServerInterface receives lines of the BOM matrix.  (For encoding 
511
        * information, see the ColonetServerInterface documentation.)  The entire matrix is passed
512
        * to the client when requested. This method takes a string of the form 
513
        * "[command code] [command code] [number of robots] [data0] [data1] ..."
514
        * with tokens separated by spaces and containing no brackets.  
515
        * The [command code]s are predefined values identifying this String as a BOM data
516
        * String, [number of robots] is an integer, and the values that follow are 
517
        * the sensor readings of the robots in order, starting with robot 0.  Only [number of robots]^2
518
        * data entries will be read.  The matrix values are saved locally until the next String is parsed.
519
        * 
520
        *
521
        * @param line the String containing BOM matrix information.
522
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots]^2 data entries in the String
523
        */
524
        public void parseMatrix (String line) {
525
                txtMatrix.setText("");
526
                String [] str = line.split(" ");
527
                int num = Integer.parseInt(str[2]);
528
                for (int i = 0; i < num; i++) {
529
                        for (int j = 0; j < num; j++) {
530
                                String next = str[3 + i*num + j];
531
                                if (next.equals("-1"))
532
                                        txtMatrix.append("-");
533
                                else 
534
                                        txtMatrix.append(next);
535
                                if (j < num - 1) 
536
                                        txtMatrix.append(" ");
537
                        }
538
                        if (i < num - 1) 
539
                                txtMatrix.append("\n");
540
                }
541
                
542
        }
543
        
544
        /**
545
        * Parses a String containing a task queue update.
546
        * Format is currently not specified.
547
        * This method currently does nothing.
548
        *
549
        * @param line the String containing task queue update information.
550
        */
551
        public void parseQueue (String line) {
552
                log.append("Got queue update\n");
553
                //TODO: display new queue data in tasks tab
554
        }
555
        
556
        /**
557
        * Parses a String containing XBee ID values.
558
        * The ColonetServerInterface receives Strings of XBee information.  (For encoding 
559
        * information, see the ColonetServerInterface documentation.)  This method takes
560
        * a string of the form "[command code] [command code] [number of robots] [id0] [id1] ..."
561
        * with tokens separated by spaces and containing no brackets.  
562
        * The [command code]s are predefined values identifying this String as an XBee
563
        * ID String, [number of robots] is an integer, and the values that follow are 
564
        * the IDs of the robots in order, starting with robot 0.  Only [number of robots] 
565
        * will be read.  The ID values are saved locally until the next String is parsed.
566
        * The purpose of having this list is to ensure that robots are properly identified for control purposes.
567
        * This keeps robot identification consistent between sessions and prevents arbitrary assignment. 
568
        *
569
        * @param line the String containing XBee ID information.
570
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots] IDs in the String
571
        * @see ColonetServerInterface#sendXBeeIDRequest()
572
        */
573
        public void parseXBeeIDs (String line) {
574
                //TODO: check if this string actually has xbee command codes
575
        
576
                String [] str = line.split(" ");
577
                int num = Integer.parseInt(str[2]);
578
                xbeeID = new int[num];
579
                for (int i = 0; i < num; i++)
580
                        xbeeID[i] = Integer.parseInt(str[i+3]);
581
                
582
                //update the list of robots to control
583
                //but save the old value first
584
                Object oldSelection = cmbRobotNum.getSelectedItem();
585
                cmbRobotNum.removeAllItems();
586
                cmbRobotNum.addItem(new String("   All   "));
587
                for (int i = 0; i < num; i++)
588
                        cmbRobotNum.addItem(new String("" + xbeeID[i]));
589
                cmbRobotNum.setSelectedItem(oldSelection);
590
        }
591
        
592
        /**
593
        * Parses a String containing battery information.
594
        * The ColonetServerInterface receives Strings of battery information.  (For encoding 
595
        * information, see the ColonetServerInterface documentation.)  This method takes
596
        * a string of the form "[command code] [command code] [robot ID] [value]"
597
        * with tokens separated by spaces and containing no brackets.  
598
        * The [command code]s are predefined values identifying this String as a battery
599
        * information String, [robot ID] is an integer, and [value] is a battery measurement.
600
        * This updates the batery information for a single robot.
601
        * 
602
        *
603
        * @param line the String containing battery information.
604
        * @see ColonetServerInterface#sendBatteryRequest(int)
605
        */
606
        public void parseBattery (String line) {
607
                System.out.println("Got battery update: " + line);
608
                String [] str = line.split(" ");
609
                int botNum = (int) line.charAt(4);
610
                int batteryVal = (int) line.charAt(6);
611
                if (cmbRobotNum != null && cmbRobotNum.getSelectedIndex()-1 == botNum) {
612
                        //TODO: update battery info graphically
613
                }
614
                // For now, just update the bar whenever we get an update.
615
                batteryIcon.setLevel((int) (100.0 * batteryVal / 255));
616
        }
617
        
618
        //
619
        // MouseListener methods
620
        //
621
        public void mouseExited(MouseEvent e) {
622
        }
623
        public void mouseEntered(MouseEvent e) {
624
        }
625
        public void mouseReleased(MouseEvent e) {
626
                vectorController.notifyMouseEvent(e, true);
627
        }
628
        public void mouseClicked(MouseEvent e) {
629
                vectorController.notifyMouseEvent(e, false);
630
        }
631
        public void mousePressed(MouseEvent e) {
632
                try {
633
                        for (int i = 0; i < numBots; i++) {
634
                                if (botRect[i].contains(e.getPoint())) {
635
                                        selectedBot = i;
636
                                        cmbRobotNum.setSelectedIndex(i+1);
637
                                }
638
                        }
639
                } catch (Exception ex) {
640
                }
641
        }
642
        public void mouseDragged(MouseEvent e) {
643
                vectorController.notifyMouseEvent(e, false);
644
        }
645
        public void mouseMoved(MouseEvent e) {
646
        }
647
        
648
        //
649
        // KeyListener methods
650
        //
651
        public void keyPressed (KeyEvent e) {
652
                int code = e.getKeyCode();
653
                if (code == KeyEvent.VK_UP) {
654
                        vectorController.setMaxForward();
655
                        vectorController.sendToServer();
656
                } else if (code == KeyEvent.VK_DOWN) {
657
                        vectorController.setMaxReverse();
658
                        vectorController.sendToServer();
659
                } else if (code == KeyEvent.VK_LEFT) {
660
                        vectorController.setMaxLeft();
661
                        vectorController.sendToServer();
662
                } else if (code == KeyEvent.VK_RIGHT) {
663
                        vectorController.setMaxRight();
664
                        vectorController.sendToServer();
665
                } else if (code == KeyEvent.VK_S) {
666
                        vectorController.setZero();
667
                        vectorController.sendToServer();
668
                }
669
        }
670
        public void keyReleased (KeyEvent e) {
671
        }
672
        public void keyTyped (KeyEvent e) {
673
        }
674
        
675
        
676
        //
677
        // ActionListener method
678
        //
679
        public void actionPerformed (ActionEvent e) {
680
                Object source = e.getSource();
681
                if (source == btnGraph) {
682
                        btnGraph.setEnabled(false);
683
                        //Start dependent threads
684
                        drawThread = new Thread(this, "drawThread");
685
                        drawThread.start();
686
                        indicator.start();
687
                        simulator.start();
688
                        webcamLoader.start();
689
                } else if (source == btnConnect) {
690
                        csi.connect(txtHost.getText(), txtPort.getText());
691
                        dataUpdater.start();
692
                        drawThread = new Thread(this, "drawThread");
693
                        drawThread.start();
694
                        indicator.start();
695
                        simulator.start();
696
                        webcamLoader.start();
697
                }
698
                // Robot Movement Controls
699
                else if (source == btnF) {
700
                        vectorController.setMaxForward();
701
                        vectorController.sendToServer();
702
                } else if (source == btnB) {
703
                        vectorController.setMaxReverse();
704
                        vectorController.sendToServer();
705
                } else if (source == btnL) {
706
                        vectorController.setMaxLeft();
707
                        vectorController.sendToServer();
708
                } else if (source == btnR) {
709
                        vectorController.setMaxRight();
710
                        vectorController.sendToServer();
711
                } else if (source == btnActivate) {
712
                        vectorController.setZero();
713
                        vectorController.sendToServer();
714
                }
715
                // Robot Commands (non-movement)
716
                else if (source == btnCommand_StopTask) {
717
                
718
                } else if (source == btnCommand_ResumeTask) {
719
                
720
                } else if (source == btnCommand_ChargeNow) {
721
                
722
                } else if (source == btnCommand_StopCharging) {
723
                
724
                }
725
                        
726
                // Queue Management
727
                else if (source == btnAddTask) {
728
                        taskAddWindow.prompt();
729
                } else if (source == btnRemoveTask) {
730
                        if (taskList.getSelectedIndex() >= 0);
731
                                csi.sendQueueRemove(taskList.getSelectedIndex());
732
                        csi.sendQueueUpdate();
733
                } else if (source == btnMoveTaskUp) {
734
                        csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
735
                        csi.sendQueueUpdate();
736
                } else if (source == btnMoveTaskDown) {
737
                        csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
738
                        csi.sendQueueUpdate();
739
                } else if (source == btnUpdateTasks) {
740
                        csi.sendQueueUpdate();
741
                }
742
        }
743
        
744
        /*
745
        *        SelectionIndicator thread.
746
        *        Graphical representation of the selection marker
747
        *
748
        *        step() and draw() are synchronized methods. step() is private and 
749
        *        used to update the position of the crosshairs. draw() is called 
750
        *        externally and should only run if all calculations in step() have
751
        *        been completed.
752
        */
753
        private class SelectionIndicator extends Thread {
754
        
755
                final int INDICATOR_DELAY = 180;
756
                final double DTHETA = 0.4;    //larger values make the marker rotate faster
757
                Graphics2D g;   //canvas to draw on
758
                boolean running;
759
                
760
                int sx, sy;                //center
761
                int r, dr;                //radius and width of marker
762
                double theta;   //current angle
763
                
764
                volatile Polygon poly1, poly2, poly3, poly4;
765
                
766
                int px1, py1;
767
                int rx1, ry1;
768
                int px2, py2;
769
                int rx2, ry2;
770
                int px3, py3;
771
                int rx3, ry3;
772
                int px4, py4;
773
                int rx4, ry4;
774
                
775
                int steps;
776
        
777
                public SelectionIndicator (Graphics2D g) {
778
                        super("SelectionIndicator");
779
                        this.g = g;
780
                        running = false;
781
                        steps = 0;
782
                        
783
                        theta = 0;
784
                        rx1 = 0; ry1 = 0;
785
                        px1 = 0; py1 = 0;
786
                        rx2 = 0; ry2 = 0;
787
                        px2 = 0; py2 = 0;
788
                        rx3 = 0; ry3 = 0;
789
                        px3 = 0; py3 = 0;
790
                        rx4 = 0; ry4 = 0;
791
                        px4 = 0; py4 = 0;
792
                }
793
                
794
                public synchronized void setCenter (int sx, int sy) {
795
                        if (sx == this.sx && sy == this.sy) return;
796
                        this.sx = sx;
797
                        this.sy = sy;
798
                        steps = 0;
799
                }
800
                
801
                public synchronized void setRadius (int r, int dr) {
802
                        this.r = r;
803
                        this.dr = dr;
804
                        steps = 0;
805
                }
806
                
807
                public void run () {
808
                        running = true;
809
                        while (running) {
810
                                step();
811
                                try { 
812
                                        Thread.sleep(INDICATOR_DELAY);
813
                                } catch (InterruptedException e) {
814
                                        running = false;
815
                                        return;
816
                                }
817
                        }
818
                }
819
                
820
                private synchronized void step () {
821
                        Polygon poly1_new = new Polygon();
822
                        Polygon poly2_new = new Polygon();
823
                        Polygon poly3_new = new Polygon();
824
                        Polygon poly4_new = new Polygon();
825
                
826
                        //the step
827
                        theta = (theta + DTHETA/Math.PI) % (Math.PI);
828
                        
829
                        //the calculation
830
                        //let p be the point of the pointy thing toward the center
831
                        //let r be the point at the opposite side
832
                        
833
                        //recalculate radius, if it will look cool, lolz
834
                        int newr = r;
835
                        if (steps < 100)
836
                        newr = (int)( r + 200/(steps+1) );
837
                        
838
                        //precompute values for dx and dy
839
                        int dx_inner = (int)(newr * Math.cos(theta));
840
                        int dy_inner = (int)(newr * Math.sin(theta));
841
                        int dx_outer = (int)((newr+dr) * Math.cos(theta));
842
                        int dy_outer = (int)((newr+dr) * Math.sin(theta));
843
                        
844
                        //calculate polygon constants
845
                        int dy_poly = (int)(dr/2 * Math.cos(theta));
846
                        int dx_poly = (int)(dr/2 * Math.sin(theta));
847
                        
848
                        //determine critical points
849
                        //kansas city shuffle!
850
                        px1 = sx + dx_inner;
851
                        py1 = sy - dy_inner;
852
                        rx1 = sx + dx_outer;
853
                        ry1 = sy - dy_outer;
854
                        px2 = sx - dx_inner;
855
                        py2 = sy + dy_inner;
856
                        rx2 = sx - dx_outer;
857
                        ry2 = sy + dy_outer;
858
                        px3 = sx - dy_inner;
859
                        py3 = sy - dx_inner;
860
                        rx3 = sx - dy_outer;
861
                        ry3 = sy - dx_outer;
862
                        px4 = sx + dy_inner;
863
                        py4 = sy + dx_inner;
864
                        rx4 = sx + dy_outer;
865
                        ry4 = sy + dx_outer;
866
                        
867
                        //create polygons
868
                        poly1_new.addPoint(px1, py1);
869
                        poly1_new.addPoint(rx1+dx_poly, ry1+dy_poly);
870
                        poly1_new.addPoint(rx1-dx_poly, ry1-dy_poly);
871
                        poly2_new.addPoint(px2, py2);
872
                        poly2_new.addPoint(rx2+dx_poly, ry2+dy_poly);
873
                        poly2_new.addPoint(rx2-dx_poly, ry2-dy_poly);
874
                        poly3_new.addPoint(px3, py3);
875
                        poly3_new.addPoint(rx3-dy_poly, ry3+dx_poly);
876
                        poly3_new.addPoint(rx3+dy_poly, ry3-dx_poly);
877
                        poly4_new.addPoint(px4, py4);
878
                        poly4_new.addPoint(rx4-dy_poly, ry4+dx_poly);
879
                        poly4_new.addPoint(rx4+dy_poly, ry4-dx_poly);
880
                        
881
                        //reassign updated polygons
882
                        poly1 = poly1_new;
883
                        poly2 = poly2_new;
884
                        poly3 = poly3_new;
885
                        poly4 = poly4_new;
886
                
887
                        if (steps < 300) steps++;
888
                }
889
                
890
                public synchronized void draw () {
891
                        if (!running) return;
892
                        g.setColor(Color.GRAY);
893
                        //draw polygons
894
                        g.fillPolygon(poly1);
895
                        g.fillPolygon(poly2);
896
                        g.fillPolygon(poly3);
897
                        g.fillPolygon(poly4);
898
                }
899
        
900
        }
901
        
902
        /*
903
        *        Simulator thread.
904
        *
905
        */
906
        private class Simulator extends Thread {
907
                final int SIMULATOR_DELAY = 300;
908
                boolean running;
909
        
910
                public Simulator () {
911
                        super("Simulator");
912
                        running = false;
913
                }
914
                
915
                public void run () {
916
                        running = true;
917
                        while (running) {
918
                                step();
919
                                try { 
920
                                        Thread.sleep(SIMULATOR_DELAY);
921
                                } catch (InterruptedException e) {
922
                                        running = false;
923
                                        return; 
924
                                }
925
                        }
926
                }
927
                
928
                private void step () {
929
                        // don't do anything! the colonet should work on its own!
930
                }
931
        
932
        }
933
        
934
        /*
935
        *        DataUpdater thread.
936
        *   The purpose of this thread is to request data from the server at regular intervals.
937
        *
938
        */
939
        class DataUpdater extends Thread {
940
                final int DATAUPDATER_DELAY = 4000;
941
                
942
                public DataUpdater () {
943
                        super("Colonet DataUpdater");
944
                }
945
                
946
                public void run () {
947
                        String line;
948
                        while (true) {
949
                                try {
950
                                        //request more data
951
                                        if (csi != null && csi.isReady()) {
952
                                                //csi.sendSensorDataRequest();
953
                                                //csi.sendXBeeIDRequest();
954
                                                if (cmbRobotNum.getSelectedIndex() > 0)
955
                                                        csi.sendBatteryRequest(cmbRobotNum.getSelectedIndex()-1);
956
                                                else
957
                                                        csi.sendBatteryRequest(200);
958
                                        }
959
                                        Thread.sleep(DATAUPDATER_DELAY);
960
                                } catch (InterruptedException e) {
961
                                        return;
962
                                } 
963
                        }
964
                }
965

    
966
        }
967
        
968
        /*
969
        *        GraphicsPanel class
970
        *        Enables more efficient image handling in a component-controlled environment
971
        */
972
        class GraphicsPanel extends JPanel {
973
                protected Image img;
974
        
975
                public GraphicsPanel (Image img) {
976
                        super();
977
                        this.img = img;
978
                }
979
                
980
                public GraphicsPanel (boolean isDoubleBuffered, Image img) {
981
                        super(isDoubleBuffered);
982
                        this.img = img;
983
                }
984
                
985
                public void paint (Graphics g) {
986
                        // Place the buffered image on the screen, inside the panel
987
                        g.drawImage(img, 0, 0, Color.WHITE, this);
988
                }
989
        
990
        }
991
        
992
        /*
993
        *        WebcamPanel class
994
        *        Enables more efficient image handling in a component-controlled environment
995
        */
996
        class WebcamPanel extends JPanel {
997
                int BORDER = 16;  // this is arbitrary. it makes the image look nice inside a border.
998
                int BOT_RADIUS = 40;
999
                volatile BufferedImage img;
1000
                volatile Point [] points;
1001
        
1002
                public WebcamPanel () {
1003
                        super();
1004
                }
1005
                
1006
                public synchronized void setImage (BufferedImage newimg) {
1007
                        if (img != null) {
1008
                                img.flush();
1009
                                img = null;
1010
                                img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
1011
                        }
1012
                        System.gc();
1013
                        img = newimg;
1014
                }
1015
                
1016
                public synchronized void setPoints (Point [] newpoints) {
1017
                        this.points = newpoints;
1018
                }
1019
                
1020
                public synchronized void paint (Graphics g) {
1021
                        
1022
                        if (img == null)
1023
                                return;
1024
                        // Place the image on the screen, inside the panel
1025
                        g.drawImage(img, 
1026
                                                BORDER,        //dx1 - the x coordinate of the first corner of the destination rectangle.
1027
                                                BORDER,        //dy1 - the y coordinate of the first corner of the destination rectangle.
1028
                                                this.getWidth() - BORDER,         //dx2 - the x coordinate of the second corner of the destination rectangle.
1029
                                                this.getHeight() - BORDER,        //dy2 - the y coordinate of the second corner of the destination rectangle.
1030
                                                0,        //sx1 - the x coordinate of the first corner of the source rectangle.
1031
                                                0,        //sy1 - the y coordinate of the first corner of the source rectangle.
1032
                                                image.getWidth(),        //sx2 - the x coordinate of the second corner of the source rectangle.
1033
                                                image.getHeight(),        //sy2 - the y coordinate of the second corner of the source rectangle.
1034
                                                null        //observer - object to be notified as more of the image is scaled and converted.
1035
                                                );
1036
                                                
1037
                        // Draw Identifiers
1038
                        if (points == null)
1039
                                return;
1040
                        g.setColor(Color.RED);
1041
                        ((Graphics2D)g).setStroke(new BasicStroke(2));
1042
                        for (int i = 0; i < points.length; i++) {
1043
                                g.drawOval(points[i].x - BOT_RADIUS, points[i].y - BOT_RADIUS, 2*BOT_RADIUS, 2*BOT_RADIUS);
1044
                        }
1045
                        
1046
                }
1047
        
1048
        }
1049
        
1050
        /*
1051
        *        WebcamLoader class
1052
        *        Handles the loading of the webcam image.
1053
        */
1054
        class WebcamLoader extends Thread 
1055
        {
1056
                final int WEBCAMLOADER_DELAY = 1000;
1057
                final String IMAGE_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet.jpg";
1058
                final String LOCATIONS_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet/locations.txt";
1059
                
1060
                URL imagePath;
1061
                URI locationsPath;
1062
                
1063
                MediaTracker mt;
1064
                BufferedImage image;
1065
                
1066
                public WebcamLoader (JApplet applet)
1067
                {
1068
                        super("ColonetWebcamLoader");
1069
                        mt = new MediaTracker(applet);
1070
                        ImageIO.setUseCache(false);
1071
                        try {
1072
                                imagePath = new URL(IMAGE_PATH);
1073
                        } catch (MalformedURLException e) {
1074
                                System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n");
1075
                        }
1076
                        try {
1077
                                locationsPath = new URI(LOCATIONS_PATH);
1078
                        } catch (URISyntaxException x) {
1079
                                System.out.println("Malformed URI: could not form URI from: [" + LOCATIONS_PATH + "]\n");
1080
                        }
1081
                        
1082
                }
1083
                
1084
                public synchronized void run ()
1085
                {
1086
                        while (true) {
1087
                                try {
1088
                                        Thread.sleep(WEBCAMLOADER_DELAY);
1089
                                        if (image != null) 
1090
                                                image.flush();
1091
                                        System.gc();
1092
                                        image = ImageIO.read(imagePath);
1093
                                        // The MediaTracker waitForID pauses the thread until the image is loaded.
1094
                                        // We don't want to display a half-downloaded image.
1095
                                        mt.addImage(image, 1);
1096
                                        mt.waitForID(1);
1097
                                        mt.removeImage(image);
1098
                                        // Save
1099
                                        panelWebcam.setImage(image);
1100
                                        parseLocations(locationsPath.toURL());
1101
                                } catch (InterruptedException e) {
1102
                                        return;
1103
                                } catch (java.security.AccessControlException e) {
1104
                                        csi.warn("java.security.AccessControlException in WebcamLoader.\n" + 
1105
                                                "The image cannot be loaded from the specified location.\n" + 
1106
                                                "Make sure you are accessing this applet from the correct server.");
1107
                                        return;
1108
                                } catch (IOException e) {
1109
                                        log.append("IOException while trying to load image.");
1110
                                }
1111
                        }
1112
                }
1113
                
1114
                private void parseLocations (URL url) {
1115
                        URLConnection conn = null;
1116
                        DataInputStream data = null;
1117
                        String line;
1118
                        String [] lines = new String[30];
1119
                        StringBuffer buf = new StringBuffer();
1120
                        int i = 0;
1121
                
1122
                        try {
1123
                                conn = url.openConnection();
1124
                                conn.connect();
1125
                                data = new DataInputStream(new BufferedInputStream(conn.getInputStream()));
1126
                                while ((line = data.readLine()) != null) {
1127
                                        buf.append(line + ";");
1128
                                        lines[i] = line;
1129
                                        i++;
1130
                                }
1131
                                data.close();
1132
                        } catch (IOException e) {
1133
                                System.out.println("IOException:" + e.getMessage());
1134
                        }
1135
                        //log.append("Got robot locations: " + buf + "\n");
1136
                        
1137
                        // Get Point values from strings
1138
                        Point [] points = new Point[i];
1139
                        for (int j = 0; j < i; j++) {
1140
                                String [] parts = lines[j].split(",");
1141
                                int xval = Integer.parseInt(parts[0]);
1142
                                int yval = Integer.parseInt(parts[1]);
1143
                                Point p = new Point(xval, yval);
1144
                                points[j] = p;
1145
                        }
1146
                        if (points.length != 0)
1147
                                panelWebcam.setPoints(points);
1148
                        
1149
                }
1150
        }
1151

    
1152
        
1153
        /*
1154
        *        VectorController class
1155
        *        Manages robot motion control graphically
1156
        */
1157
        class VectorController extends GraphicsPanel {
1158
                int x, y, cx, cy;
1159
                int width, height;
1160
                int side;
1161
                
1162
                public VectorController (Image img) {
1163
                        super (img);
1164
                        width = img.getWidth(null);
1165
                        height = img.getHeight(null);
1166
                        cx = img.getWidth(null)/2;
1167
                        cy = img.getHeight(null)/2;
1168
                        x = cx;
1169
                        y = cy;
1170
                        if (width < height)
1171
                                side = width;
1172
                        else
1173
                                side = height;
1174
                }
1175
                
1176
                public void setPoint (int x, int y) {
1177
                        if (!isValidPoint(x, y))
1178
                                return;
1179
                        this.x = x;
1180
                        this.y = y;
1181
                        repaint();
1182
                }
1183
                
1184
                public boolean isValidPoint (int x, int y) {
1185
                        double xterm = Math.pow(1.0*(x - cx)/(side/2), 2);
1186
                        double yterm = Math.pow(1.0*(y - cy)/(side/2), 2);
1187
                        return (xterm + yterm <= 1);
1188
                }
1189
                
1190
                public void notifyMouseEvent (MouseEvent e, boolean send) {
1191
                        if (!isValidPoint(e.getX(), e.getY()))
1192
                                return;
1193
                        vectorController.setPoint(e.getX(), e.getY());
1194
                        vectorController.repaint();
1195
                        if (send)
1196
                                vectorController.sendToServer();
1197
                }
1198
                
1199
                public int getSpeed () {
1200
                        int dx = x - cx;
1201
                        int dy = y - cy;
1202
                        int v = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) );
1203
                        return v;
1204
                }
1205
                
1206
                public int getAngle () {
1207
                        int dx = x - cx;
1208
                        int dy = cy - y;
1209
                        double theta = Math.atan2(Math.abs(dx), Math.abs(dy));
1210
                        theta = (int) (1.0 * theta * 180 / Math.PI);  //transform to degrees
1211
                        if (dy < 0)
1212
                                theta = 180 - theta;
1213
                        theta *= Math.signum(dx);
1214
                        return (int) theta;
1215
                }
1216
                
1217
                public void paint (Graphics g) {
1218
                        g.setColor(Color.BLACK);
1219
                        g.fillRect(0, 0, width, height);
1220
                        ((Graphics2D)g).setStroke(new BasicStroke(1));
1221
                        g.setColor(Color.RED);
1222
                        g.drawOval(cx-side/2, cy-side/2, side, side);
1223
                        ((Graphics2D)g).setStroke(new BasicStroke(2));
1224
                        g.setColor(Color.GREEN);
1225
                        g.drawLine(cx, cy, x, y);
1226
                        g.fillOval(x-3, y-3, 6, 6);
1227
                }
1228
                
1229
                public void setMaxForward () {
1230
                        setPoint(cx, cy - (side/2) + 1);
1231
                }
1232
                
1233
                public void setMaxReverse () {
1234
                        setPoint(cx, cy + (side/2) - 1);
1235
                }
1236
                
1237
                public void setMaxLeft () {
1238
                        setPoint(cx - (side/2) + 1, cy);
1239
                }
1240
                
1241
                public void setMaxRight () {
1242
                        setPoint(cx + (side/2) - 1, cy);
1243
                }
1244
                
1245
                public void setZero () {
1246
                        setPoint(cx, cy);
1247
                }
1248
                
1249
                public void sendToServer () {
1250
                        //log.append("Attempitng to send angle = " + vectorController.getAngle() + ", velocity = " + vectorController.getVelocity() + "\n");
1251
                        String dest = ColonetServerInterface.GLOBAL_DEST;
1252
                        if (cmbRobotNum != null && cmbRobotNum.getSelectedIndex() > 0)
1253
                                dest = "" + (cmbRobotNum.getSelectedIndex()-1);
1254
                        
1255
                        if (csi != null) {
1256
                                if (cx == 0 && cy == 0) {
1257
                                        csi.sendData(ColonetServerInterface.MOTORS_OFF, dest);
1258
                                } else {
1259
                                        //Directional commands
1260
                                        
1261
                                        if (x > cx && y == cy) {  //move right
1262
                                                csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", dest);
1263
                                                csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", dest);
1264
                                        } else if (x < cx && y == cy) {  //move left
1265
                                                csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", dest);
1266
                                                csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", dest);
1267
                                        } else if (x == cx && y > cy) {  //move forward
1268
                                                csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 225", dest);
1269
                                                csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 225", dest);
1270
                                        } else if (x == cx && y < cy) {  //move backward
1271
                                                csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 225", dest);
1272
                                                csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 225", dest);
1273
                                        } else if (x == cx && y == cy) {  //stop!
1274
                                                csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 0", dest);
1275
                                                csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 0", dest);
1276
                                        }
1277
                                        
1278
                                        
1279
                                }
1280
                        }
1281
                                
1282
                        //The move command doesn't really work...or does it?
1283
                        /*
1284
                        csi.sendData(ColonetServerInterface.MOVE + " " + 
1285
                                vectorController.getSpeed() + " " + 
1286
                                vectorController.getAngle(), dest);
1287
                        */        
1288
                        
1289
                }
1290
        
1291
        }
1292
        
1293
        /*
1294
        *        TaskAddWindow class
1295
        *        makes it easy to add tasks to the queue
1296
        */
1297
        class TaskAddWindow extends JFrame implements ActionListener, ListSelectionListener {
1298
                JPanel panelButtons;
1299
                JPanel panelParameters;
1300
                JPanel panelSouth;
1301
                JPanel panelSelection;
1302
                JButton btnSubmit;
1303
                JButton btnCancel;
1304
                DefaultListModel availableListModel;
1305
                JList availableList;
1306
                JScrollPane spAvailableTasks;
1307
                JTextArea txtDescription;
1308
                JTextField txtParameters;
1309
                MouseListener mouseListener;
1310
                
1311
                public TaskAddWindow () {
1312
                        super("Add a Task");
1313
                        super.setSize(500,500);
1314
                        super.setLayout(new BorderLayout());
1315
                        
1316
                        // set up buttons
1317
                        btnSubmit = new JButton("Submit");
1318
                        btnCancel = new JButton("Cancel");
1319
                        panelButtons = new JPanel();
1320
                        panelButtons.setLayout(new FlowLayout());
1321
                        panelButtons.add(btnSubmit);
1322
                        panelButtons.add(btnCancel);
1323
                        this.getRootPane().setDefaultButton(btnSubmit);
1324
                        
1325
                        // set up task list
1326
                        availableListModel = new DefaultListModel();
1327
                        availableListModel.addElement("Map the Environment");
1328
                        availableListModel.addElement("Clean Up Chemical Spill");
1329
                        availableListModel.addElement("Grow Plants");
1330
                        availableListModel.addElement("Save the Cheerleader");
1331
                        availableListModel.addElement("Save the World");
1332
                        availableList = new JList(availableListModel);
1333
                        availableList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1334
                        availableList.setSelectedIndex(-1);
1335
                        spAvailableTasks = new JScrollPane(availableList);
1336
                        spAvailableTasks.setBorder(BorderFactory.createTitledBorder("Select A Task"));
1337
                        txtDescription = new JTextArea();
1338
                        txtDescription.setEditable(false);
1339
                        txtDescription.setLineWrap(true);
1340
                        txtDescription.setWrapStyleWord(true);
1341
                        txtDescription.setBorder(BorderFactory.createTitledBorder("Description"));
1342
                        
1343
                        //set up parameter area
1344
                        panelParameters = new JPanel();
1345
                        panelParameters.setLayout(new BorderLayout());
1346
                        txtParameters = new JTextField();
1347
                        panelParameters.add(new JLabel("Optional parameters for this task: "), BorderLayout.WEST);
1348
                        panelParameters.add(txtParameters);
1349
                        
1350
                        // assemble objects
1351
                        panelSelection = new JPanel();
1352
                        panelSelection.setLayout(new GridLayout(1,2));
1353
                        panelSelection.add(spAvailableTasks);
1354
                        panelSelection.add(txtDescription);
1355
                        
1356
                        panelSouth = new JPanel();
1357
                        panelSouth.setLayout(new GridLayout(2,1));
1358
                        panelSouth.add(panelParameters);
1359
                        panelSouth.add(panelButtons);
1360
                        
1361
                        this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
1362
                        this.getContentPane().add(panelSelection, BorderLayout.CENTER);
1363
                        this.setLocationRelativeTo(null);
1364
                        
1365
                        // add listeners here
1366
                        availableList.addListSelectionListener(this);
1367
                        btnSubmit.addActionListener(this);
1368
                        btnCancel.addActionListener(this);
1369
                }
1370
                
1371
                public void prompt () {
1372
                        this.setVisible(true);
1373
                }
1374
                
1375
                private String getDescription (int index) {
1376
                        if (index < 0)
1377
                                return "";
1378
                        switch (index) {
1379
                                case 0: return "SLAM and junk";
1380
                                case 1: return "I'm not sure this works";
1381
                                case 2: return "Push them into the light";
1382
                                case 3: return "...";
1383
                                case 4: return "...";
1384
                        
1385
                                default: return "Task not recognized";
1386
                        }
1387
                }
1388
                
1389
                public void actionPerformed (ActionEvent e) {
1390
                        Object source = e.getSource();
1391
                        if (source == btnSubmit) {
1392
                                txtParameters.setText(txtParameters.getText().trim());
1393
                                
1394
                                
1395
                                this.setVisible(false);
1396
                        } else if (source == btnCancel) {
1397
                                this.setVisible(false);
1398
                        }
1399
                }
1400
                
1401
                public void valueChanged (ListSelectionEvent e) {
1402
                        int index = availableList.getSelectedIndex();
1403
                        if (index >= 0)
1404
                                txtDescription.setText(getDescription(index));
1405
                }
1406
        
1407
        }
1408
        
1409
        class BatteryIcon implements Icon{
1410
                private int width;
1411
            private int height;
1412
            private int level;
1413
            
1414
            public BatteryIcon(){
1415
                    width = 100;
1416
                    height =100;
1417
                    level = 100;
1418
            }
1419
            
1420
            public BatteryIcon(int startLevel){
1421
                    width = 50;
1422
                    height = 50;
1423
                    level = startLevel;
1424
            }
1425
            
1426
            public BatteryIcon(int startLevel, int w, int h){
1427
                    level = startLevel;
1428
                    width = w;
1429
                    height = h;
1430
            }
1431
            
1432
            public void paintIcon(Component c, Graphics g, int x, int y) {
1433
                Graphics2D g2d = (Graphics2D) g.create();
1434
                //background
1435
                g2d.setColor(Color.WHITE);
1436
                g2d.fillRect(x +1 ,y + 1,width-2,height-2);
1437
                //outline
1438
                g2d.setColor(Color.BLACK);
1439
                g2d.drawRect((int)(x + width*.3) ,y + 2,(int)(width*.4),height -4);
1440
                //battery life rectangle
1441
                g2d.setColor(Color.GREEN);
1442
                int greenX = (int)(x + 1 + width*.3);
1443
                int greenY = (int)((y+3) + Math.abs(level-100.0)*(height-6)/(100));
1444
                int greenWidth = (int)(width*.4 - 2)+1;
1445
                int greenHeight = 1+(int)(level-0.0)*(height-6)/(100);
1446
                g2d.fillRect(greenX, greenY, greenWidth, greenHeight);
1447
                //text
1448
                g2d.setColor(Color.BLACK);
1449
                g2d.drawString(level + "%", greenX + greenWidth/2 - 10, greenY + greenHeight/2 + 5);
1450
                
1451
                g2d.dispose();
1452
            }
1453
            
1454
            public void setLevel(int newLevel) {
1455
                    level = newLevel;
1456
            }
1457
            
1458
            public int getIconWidth() {
1459
                return width;
1460
            }
1461
            
1462
            public int getIconHeight() {
1463
                return height;
1464
            }
1465
        }
1466

    
1467

    
1468
}