Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (45.4 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 = 250;
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("localhost");
177
                txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
178
                txtPort = new JTextField("10123");
179
                txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
180
                btnConnect = new JButton("Connect");
181
                getRootPane().setDefaultButton(btnConnect);
182
                lblConnectionStatus = new JLabel("Status: Offline");
183
                panelConnect = new JPanel();
184
                panelConnect.setLayout(new GridLayout(6,1));
185
                panelConnect.add(lblConnectionStatus);
186
                panelConnect.add(txtHost);
187
                panelConnect.add(txtPort);
188
                panelConnect.add(btnConnect);
189
                //panelConnect.add(btnGraph);
190
                panelServerInterface = new JPanel();
191
                panelServerInterface.setLayout(new GridLayout(2,1));
192
                panelServerInterface.add(panelConnect);
193
                panelServerInterface.add(txtMatrix);
194
        
195
                // Robot direction panel
196
                panelRobotDirection = new JPanel();
197
                panelRobotDirectionButtons = new JPanel();
198
                btnF = new JButton("^");
199
                btnB = new JButton("v");
200
                btnL = new JButton("<");
201
                btnR = new JButton(">");
202
                btnActivate = new JButton("o");
203
                panelRobotDirectionButtons.setLayout(new GridLayout(1,5));
204
                panelRobotDirectionButtons.add(btnActivate);
205
                panelRobotDirectionButtons.add(btnF);
206
                panelRobotDirectionButtons.add(btnB);
207
                panelRobotDirectionButtons.add(btnL);
208
                panelRobotDirectionButtons.add(btnR);
209
                
210
                imageVectorControl = gc.createCompatibleImage(VECTOR_CONTROLLER_WIDTH, VECTOR_CONTROLLER_HEIGHT);
211
                vectorController = new VectorController(imageVectorControl);
212
                panelRobotDirection.setLayout(new BorderLayout());
213
                panelRobotDirection.add(vectorController, BorderLayout.CENTER);
214
                panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
215
                
216
                // Robot Control and Commands
217
                panelRobotCommands = new JPanel();
218
                panelRobotCommands.setLayout(new GridLayout(5,2));
219
                cmbRobotNum = new JComboBox();
220
                // Battery subset
221
                lblBattery = new JLabel();
222
                batteryIcon = new BatteryIcon(25);
223
                //batteryIcon = new BatteryIcon(50, lblBattery.getMaximumSize().height, lblBattery.getMaximumSize().height);
224
                lblBattery = new JLabel(batteryIcon);
225
                btnCommand_StopTask = new JButton("Stop Current Task");
226
                btnCommand_ResumeTask = new JButton("Resume Current Task");
227
                btnCommand_ChargeNow = new JButton("Recharge Now");
228
                btnCommand_StopCharging = new JButton("Stop Recharging");
229
                panelRobotCommands.add(new JLabel("Select Robot to Control: "));
230
                panelRobotCommands.add(cmbRobotNum);
231
                panelRobotCommands.add(new JLabel("Battery Level: "));
232
                panelRobotCommands.add(lblBattery);
233
                panelRobotCommands.add(btnCommand_StopTask);
234
                panelRobotCommands.add(btnCommand_ResumeTask);
235
                panelRobotCommands.add(btnCommand_ChargeNow);
236
                panelRobotCommands.add(btnCommand_StopCharging);
237
                panelRobotControl = new JPanel();
238
                panelRobotControl.setLayout(new GridLayout(2,1));
239
                panelRobotControl.add(panelRobotDirection);
240
                panelRobotControl.add(panelRobotCommands);
241
                
242
                
243
                // Task Manager
244
                panelTaskManager = new JPanel();
245
                panelTaskManager.setLayout(new BorderLayout());
246
                taskListModel = new DefaultListModel();
247
                taskList = new JList(taskListModel);
248
                taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
249
                taskList.setSelectedIndex(0);
250
                spTaskManager = new JScrollPane(taskList);
251
                panelTaskManagerControls = new JPanel();
252
                panelTaskManagerControls.setLayout(new GridLayout(1,4));
253
                panelTaskManagerControlsPriority = new JPanel();
254
                panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
255
                btnAddTask = new JButton("Add...");
256
                btnRemoveTask = new JButton("Remove");
257
                btnMoveTaskUp = new JButton("^");
258
                btnMoveTaskDown = new JButton("v");
259
                btnUpdateTasks = new JButton("Update");
260
                panelTaskManagerControlsPriority.add(btnMoveTaskUp);
261
                panelTaskManagerControlsPriority.add(btnMoveTaskDown);
262
                panelTaskManagerControls.add(btnAddTask);
263
                panelTaskManagerControls.add(btnRemoveTask);
264
                panelTaskManagerControls.add(btnUpdateTasks);
265
                panelTaskManagerControls.add(panelTaskManagerControlsPriority);
266
                panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
267
                panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
268
                panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
269
                taskAddWindow = new TaskAddWindow();
270
                
271
                // Message log
272
                log = new JTextArea();
273
                spLog = new JScrollPane(log,
274
                        ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 
275
                        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
276
                spLog.setBorder(BorderFactory.createTitledBorder("Log"));
277
                spLog.setPreferredSize(new Dimension(0, 120));
278
                log.setEditable(false);
279
                
280
                // Main control mechanism
281
                panelControl = new JPanel();
282
                panelControl.setLayout(new GridLayout(1,1));
283
                tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
284
                tabPaneControl.setPreferredSize(new Dimension(VECTOR_CONTROLLER_WIDTH, 0));
285
                tabPaneControl.addTab("Connection", panelServerInterface);
286
                tabPaneControl.addTab("Robots", panelRobotControl);
287
                tabPaneControl.addTab("Tasks", panelTaskManager);
288
                panelControl.add(tabPaneControl);
289
                
290
                // Set up elements in the south
291
                panelSouth = new JPanel();
292
                panelSouth.setLayout(new GridLayout(1,2));
293
                //panelSouth.add(spLog);
294

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

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

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

    
1502

    
1503
}