Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (45.6 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
*        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 the robot graph
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 = 200;
33
        
34

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

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

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

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

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

    
1507

    
1508
}