Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (31.2 KB)

1
//
2
//  Colonet.java
3
//
4

    
5
import javax.swing.*;
6
import javax.swing.event.*;
7
import java.awt.*;
8
import java.awt.image.*;
9
import java.awt.event.*;
10
import java.net.*;
11
import java.io.*;
12
import java.util.Random;
13
import java.applet.*;
14

    
15
public class Colonet extends JApplet implements ActionListener, MouseInputListener, Runnable {
16

    
17
        final int CANVAS_SIZE = 500;  //the applet may be slow if the canvas gets too large
18
        final int BUFFER = 50;
19
        final int RADIUS = 30;
20

    
21
        // Connection
22
        JTextField txtHost;                                
23
        JTextField txtPort;                                
24
        JButton btnConnect;        
25
        JButton btnGraph;
26
        JLabel lblConnectionStatus;
27
        JTextArea txtMatrix;
28
        JTextArea txtInfo; 
29
        JPanel panelConnect;
30
        JPanel panelServerInterface;
31
        
32
        // Stats
33
        JLabel lblBattery;
34
        JLabel lblTokenPasses;
35
        JLabel lblHastToken;
36
        JPanel panelStats;
37
        
38
        // South
39
        JPanel panelSouth;
40
        JTextArea log;
41
        JScrollPane spLog;
42
        
43
        // Control
44
        JPanel panelControl;
45
        JTabbedPane tabPaneControl;
46
        JPanel panelRobotControl;
47
        JPanel panelRobotDirection;
48
        JPanel panelRobotDirectionButtons;
49
        JPanel panelRobotCommands;
50
        JButton btnF, btnB, btnL, btnR, btnActivate;
51
        JComboBox cmbRobotNum;
52
        VectorController vectorController;
53
        BufferedImage imageVectorControl;
54
        
55
        // Task Manager
56
        JPanel panelTaskManager;
57
        JScrollPane spTaskManager;
58
        JPanel panelTaskManagerControls;
59
        JPanel panelTaskManagerControlsPriority;
60
        DefaultListModel taskListModel;
61
        JList taskList;
62
        JButton btnAddTask;
63
        JButton btnRemoveTask;
64
        JButton btnMoveTaskUp;
65
        JButton btnMoveTaskDown;
66
        JButton btnUpdateTasks;
67
        TaskAddWindow taskAddWindow;
68
        
69
        // Graphics
70
        ImagePanel panel;
71
        GraphicsConfiguration gc;
72
        volatile BufferedImage image;
73
        volatile Graphics2D canvas;
74
        int cx, cy;
75
        
76
        Socket socket;                                        
77
        OutputStreamWriter out;
78
        DataUpdater dataUpdater;  
79
        
80
        Font botFont;
81
        Random random = new Random();
82
        volatile int tokenLoc;  //the token is currently here
83
        volatile int numBots;
84
        volatile int selectedBot;  //the user has selected this bot
85
        volatile Rectangle[] botRect;  //contains boundary shapes around bots for click detection
86
        volatile int[] xbeeID;
87
        
88
        Thread drawThread;
89
        Simulator simulator;
90
        SelectionIndicator indicator;
91
        PacketMonitor packetMonitor;
92
        ColonetServerInterface csi;
93

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

    
130
        private synchronized void createAndShowGUI () {
131
                // init graphical elements
132
                // Get the graphics configuration of the screen to create a buffer
133
                gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
134
                        .getDefaultScreenDevice().getDefaultConfiguration();
135
                image = gc.createCompatibleImage(CANVAS_SIZE,CANVAS_SIZE);
136
                canvas = image.createGraphics();
137
                canvas.setStroke(new BasicStroke(2));  //set pen width
138
                panel = new ImagePanel(false, image);  //set automatic double-buffering to false. we are doing it manually.
139
                
140
                // Calculate center of canvas
141
                cx = image.getWidth() / 2;
142
                cy = image.getHeight() / 2;
143
                
144
                botFont = new Font("Arial", Font.PLAIN, 14);
145
                tokenLoc = 0;
146
                numBots = 0;
147
                selectedBot = 0;
148
                
149
                // Connection area
150
                txtMatrix = new JTextArea();
151
                txtMatrix.setBorder(BorderFactory.createTitledBorder("Input Matrix"));
152
                txtInfo = new JTextArea();
153
                txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
154
                txtInfo.setEditable(false);
155
                btnGraph = new JButton("Run");
156
                txtHost = new JTextField("roboclub9.frc.ri.cmu.edu");
157
                txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
158
                txtPort = new JTextField("10123");
159
                txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
160
                btnConnect = new JButton("Connect");
161
                lblConnectionStatus = new JLabel("Status: Offline");
162
                panelConnect = new JPanel();
163
                panelConnect.setLayout(new GridLayout(6,1));
164
                panelConnect.add(lblConnectionStatus);
165
                panelConnect.add(txtHost);
166
                panelConnect.add(txtPort);
167
                panelConnect.add(btnConnect);
168
                panelConnect.add(txtInfo);
169
                panelConnect.add(btnGraph);
170
                panelServerInterface = new JPanel();
171
                panelServerInterface.setLayout(new GridLayout(2,1));
172
                panelServerInterface.add(panelConnect);
173
                panelServerInterface.add(txtMatrix);
174
                                
175
                // Status Elements
176
                lblTokenPasses = new JLabel();
177
                lblBattery = new JLabel("???");
178
                panelStats = new JPanel();
179
                panelStats.setLayout(new GridLayout(4,2));
180
                panelStats.add(new JLabel("Token Passes / sec          "));
181
                panelStats.add(lblTokenPasses);
182
                panelStats.add(new JLabel("Battery     "));
183
                panelStats.add(lblBattery);
184
                panelStats.add(new JLabel("Token Passes / sec     "));
185
                panelStats.add(lblTokenPasses);
186
                
187
                //TODO: add panelStats somewhere?
188

    
189
                // Robot direction panel
190
                panelRobotDirection = new JPanel();
191
                panelRobotDirectionButtons = new JPanel();
192
                btnF = new JButton("^");
193
                btnB = new JButton("v");
194
                btnL = new JButton("<");
195
                btnR = new JButton(">");
196
                btnActivate = new JButton("o");
197
                panelRobotDirectionButtons.setLayout(new GridLayout(1,5));
198
                panelRobotDirectionButtons.add(btnActivate);
199
                panelRobotDirectionButtons.add(btnF);
200
                panelRobotDirectionButtons.add(btnB);
201
                panelRobotDirectionButtons.add(btnL);
202
                panelRobotDirectionButtons.add(btnR);
203
                
204
                imageVectorControl = gc.createCompatibleImage(380, 210);
205
                vectorController = new VectorController(imageVectorControl);
206
                panelRobotDirection.setLayout(new BorderLayout());
207
                panelRobotDirection.add(vectorController, BorderLayout.CENTER);
208
                panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
209
                
210
                // Robot Control and Commands
211
                panelRobotCommands = new JPanel();
212
                panelRobotCommands.setLayout(new FlowLayout());
213
                cmbRobotNum = new JComboBox();
214
                panelRobotCommands.add(cmbRobotNum);
215
                panelRobotCommands.add(new JLabel("Commands go here"));
216
                panelRobotControl = new JPanel();
217
                panelRobotControl.setLayout(new GridLayout(2,1));
218
                panelRobotControl.add(panelRobotDirection);
219
                panelRobotControl.add(panelRobotCommands);
220
                
221
                // Task Manager
222
                panelTaskManager = new JPanel();
223
                panelTaskManager.setLayout(new BorderLayout());
224
                taskListModel = new DefaultListModel();
225
                taskList = new JList(taskListModel);
226
                taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
227
                taskList.setSelectedIndex(0);
228
                spTaskManager = new JScrollPane(taskList);
229
                panelTaskManagerControls = new JPanel();
230
                panelTaskManagerControls.setLayout(new GridLayout(1,4));
231
                panelTaskManagerControlsPriority = new JPanel();
232
                panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
233
                btnAddTask = new JButton("Add...");
234
                btnRemoveTask = new JButton("Remove");
235
                btnMoveTaskUp = new JButton("^");
236
                btnMoveTaskDown = new JButton("v");
237
                btnUpdateTasks = new JButton("Update");
238
                panelTaskManagerControlsPriority.add(btnMoveTaskUp);
239
                panelTaskManagerControlsPriority.add(btnMoveTaskDown);
240
                panelTaskManagerControls.add(btnAddTask);
241
                panelTaskManagerControls.add(btnRemoveTask);
242
                panelTaskManagerControls.add(btnUpdateTasks);
243
                panelTaskManagerControls.add(panelTaskManagerControlsPriority);
244
                panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
245
                panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
246
                panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
247
                
248
                // Task Manager Add Window
249
                taskAddWindow = new TaskAddWindow();
250
                
251
                // Message log
252
                log = new JTextArea();
253
                spLog = new JScrollPane(log,
254
                        ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 
255
                        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
256
                spLog.setBorder(BorderFactory.createTitledBorder("Log"));
257
                spLog.setPreferredSize(new Dimension(0, 150));
258
                log.setEditable(false);
259
                
260
                // Main control mechanism
261
                panelControl = new JPanel();
262
                panelControl.setLayout(new GridLayout(1,1));
263
                tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
264
                tabPaneControl.setPreferredSize(new Dimension(400, 0));
265
                tabPaneControl.addTab("Connection", panelServerInterface);
266
                tabPaneControl.addTab("Robots", panelRobotControl);
267
                tabPaneControl.addTab("Tasks", panelTaskManager);
268
                panelControl.add(tabPaneControl);
269
                
270
                // Set up elements in the south
271
                panelSouth = new JPanel();
272
                panelSouth.setLayout(new GridLayout(1,2));
273
                panelSouth.add(spLog);
274

    
275
                this.getContentPane().setLayout(new BorderLayout());
276
                this.getContentPane().add(panel, BorderLayout.CENTER);
277
                this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
278
                this.getContentPane().add(panelControl, BorderLayout.EAST);
279
                this.setVisible(true);
280
                
281
                /* Add all listeners here */
282
                // Task Management
283
                btnAddTask.addActionListener(this);
284
                btnRemoveTask.addActionListener(this);
285
                btnMoveTaskUp.addActionListener(this);
286
                btnMoveTaskDown.addActionListener(this);
287
                btnUpdateTasks.addActionListener(this);
288
                // Robot Control
289
                btnF.addActionListener(this);
290
                btnB.addActionListener(this);
291
                btnL.addActionListener(this);
292
                btnR.addActionListener(this);
293
                btnActivate.addActionListener(this);
294
                vectorController.addMouseMotionListener(this);
295
                vectorController.addMouseListener(this);
296
                // Other
297
                btnGraph.addActionListener(this);
298
                btnConnect.addActionListener(this);
299
                panel.addMouseListener(this);
300
                this.addMouseMotionListener(this);        
301
                
302
                                
303
                // Set up dependent threads
304
                indicator = new SelectionIndicator(canvas);
305
                indicator.setRadius(RADIUS+3, 15);  //a tad more than the bot radius
306
                packetMonitor = new PacketMonitor();
307
                simulator = new Simulator();
308
                
309
                csi = new ColonetServerInterface(this);
310
        
311
        }
312
        
313
        public synchronized void paint (Graphics g) {
314
                /*        Redraw the graphical components in the applet. 
315
                        This paint method overrides the built-in paint of the 
316
                        JApplet, and we don't want to deal with redrawing the
317
                        components manually. Fuck that shit. */
318
                step();
319
                super.paint(g);
320
        }
321
        
322
        public synchronized void update (Graphics g) {
323
                paint(g);
324
        }
325
        
326
        public void actionPerformed (ActionEvent e) {
327
                Object source = e.getSource();
328
                if (source == btnGraph) {
329
                        btnGraph.setEnabled(false);
330
                        //Start dependent threads
331
                        drawThread = new Thread(this, "drawThread");
332
                        drawThread.start();
333
                        indicator.start();
334
                        packetMonitor.start();
335
                        simulator.start();
336
                } else if (source == btnConnect) {
337
                        csi.connect(txtHost.getText(), txtPort.getText());
338
                        dataUpdater = new DataUpdater();
339
                        dataUpdater.start();
340
                }
341
                // Robot Movement Controls
342
                else if (source == btnF) {
343
                        vectorController.setMaxForward();
344
                        vectorController.sendToServer();
345
                } else if (source == btnB) {
346
                        vectorController.setMaxReverse();
347
                        vectorController.sendToServer();
348
                } else if (source == btnL) {
349
                        vectorController.setMaxLeft();
350
                        vectorController.sendToServer();
351
                } else if (source == btnR) {
352
                        vectorController.setMaxRight();
353
                        vectorController.sendToServer();
354
                } else if (source == btnActivate) {
355
                        vectorController.setZero();
356
                        vectorController.sendToServer();
357
                }
358
                        
359
                // Queue Management
360
                else if (source == btnAddTask) {
361
                        taskAddWindow.prompt();
362
                } else if (source == btnRemoveTask) {
363
                        if (taskList.getSelectedIndex() >= 0);
364
                                csi.sendQueueRemove(taskList.getSelectedIndex());
365
                        csi.sendQueueUpdate();
366
                } else if (source == btnMoveTaskUp) {
367
                        csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
368
                        csi.sendQueueUpdate();
369
                } else if (source == btnMoveTaskDown) {
370
                        csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
371
                        csi.sendQueueUpdate();
372
                } else if (source == btnUpdateTasks) {
373
                        csi.sendQueueUpdate();
374
                }
375
        }
376
        
377
        private void randomize () {
378
                Random r = new Random();
379
                StringBuilder s = new StringBuilder();
380
                
381
                int count = r.nextInt(8) + 1;
382
                for (int i = 0; i < count; i++) {
383
                        for (int j = 0; j < count; j++) {
384
                                if (r.nextBoolean())        
385
                                        s.append("" + (r.nextInt(16) + 1));
386
                                else 
387
                                        s.append("-");
388
                                if (j != count-1)
389
                                        s.append(" ");
390
                        }
391
                        if (i != count-1) s.append("\n");
392
                }
393
                
394
                txtMatrix.setText(s.toString());
395
        }
396
        
397
        public void drawRobot (int id, int x, int y) {
398
                //save the bot in memory, so we can tell if we click on it later
399
                botRect[id] = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS);
400
        
401
                //draw the bot on the canvas
402
                canvas.setColor(Color.BLACK);
403
                canvas.drawOval(x-RADIUS, y-RADIUS, RADIUS*2, RADIUS*2);
404
                
405
                //draw the label
406
                canvas.setFont(botFont);
407
                try {
408
                        canvas.drawString("" + xbeeID[id], x-20, y+2);
409
                } catch (Exception e) {
410
                        canvas.drawString("???", x-22, y+2);
411
                }
412
        }
413
        
414
        public void drawConnection (int start, int end, int radius, Color color) {
415
                final int ARROW_LENGTH = 18;
416
        
417
                double angle = 2.0 * Math.PI / numBots;
418
                int startx, starty, endx, endy;
419
                startx = cx - (int)(radius * Math.cos(start * angle));
420
                starty = cy - (int)(radius * Math.sin(start * angle));
421
                endx = cx - (int)(radius * Math.cos(end * angle));
422
                endy = cy - (int)(radius * Math.sin(end * angle));
423
                canvas.setColor(color);
424
                canvas.drawLine(startx, starty, endx, endy);
425
                
426
                //create arrow
427
                if (color.equals(Color.BLACK)) return;
428
                int big_dy = starty - endy;
429
                int big_dx = endx - startx;
430
                double theta = 0;
431
                if (big_dx == 0 && starty > endy) //pointing up
432
                        theta = Math.PI/2;
433
                else if (big_dx == 0 && starty < endy) //pointing down 
434
                        theta = 3*Math.PI/2;
435
                else if (big_dy == 0 && startx > endx) //pointing left
436
                        theta = Math.PI;
437
                else if (big_dy == 0 && startx < endx) //pointing right
438
                        theta = 0;
439
                else
440
                        theta = Math.atan(1.0 * big_dy / big_dx);
441
                
442
                //create ploygon
443
                Polygon poly = new Polygon();
444
                int dx_arrow = Math.abs((int)(ARROW_LENGTH * Math.cos(theta)));
445
                int dy_arrow = Math.abs((int)(ARROW_LENGTH * Math.sin(theta)));
446
                int dy_half = (int)(ARROW_LENGTH/2 * Math.cos(theta));
447
                int dx_half = (int)(ARROW_LENGTH/2 * Math.sin(theta));
448
                int rx = (big_dx > 0) ? endx - dx_arrow : endx + dx_arrow;
449
                int ry = (big_dy > 0) ? endy + dy_arrow : endy - dy_arrow;
450
                poly.addPoint(endx, endy);
451
                poly.addPoint(rx - dx_half, ry - dy_half);
452
                poly.addPoint(rx + dx_half, ry + dy_half);
453
                canvas.fillPolygon(poly);
454
        }
455
        
456
        public void run () {
457
                while (true) {
458
                        repaint();
459
                        try { 
460
                                Thread.sleep(90);
461
                        } catch (InterruptedException e) {
462
                                return;
463
                        }
464
                }
465
        }
466
        
467
        public void step () {
468
                final int DIAMETER = image.getWidth() - 2*BUFFER;
469
                final int BIGRADIUS = DIAMETER / 2;
470
                final int TOKENRADIUS = 40;
471
                boolean valid;
472
        
473
                // clear image
474
                canvas.setColor(Color.WHITE);
475
                canvas.fillRect(0, 0, image.getWidth(), image.getHeight());
476
                
477
                // parse the matrix, to see what robots exist
478
                String [] rows = txtMatrix.getText().split("\n");
479
                numBots = rows.length;
480
                String [][] entries = new String[numBots][numBots];
481
                valid = true;
482
                for (int i = 0; i < numBots; i++) {
483
                        entries[i] = rows[i].split(" ");
484
                        if (entries[i].length != rows.length) valid = false;
485
                }
486
                
487
                if (valid) {
488
                        this.showStatus("Running");
489
                        
490
                        // draw robots and find which one is seleced
491
                        double angle = 2.0 * Math.PI / numBots;
492
                        canvas.setColor(Color.BLACK);
493
                        botRect = new Rectangle[numBots];
494
                        int x, y;
495
                        if (selectedBot >= numBots) selectedBot = 0;
496
                        for (int i = 0; i < numBots; i++) {
497
                                x = cx - (int)(BIGRADIUS * Math.cos(i * angle));
498
                                y = cy - (int)(BIGRADIUS * Math.sin(i * angle));
499
                                drawRobot(i, x, y);
500
                                if (i == selectedBot) indicator.setCenter(x, y);
501
                        }
502
                        
503
                        // draw token marker
504
                        int tokenx, tokeny;
505
                        int tokenNum = tokenLoc;
506
                        tokenx = cx - (int)(BIGRADIUS * Math.cos(tokenNum * angle));
507
                        tokeny = cy - (int)(BIGRADIUS * Math.sin(tokenNum * angle));
508
                        canvas.setColor(Color.RED);
509
                        canvas.drawOval(tokenx-TOKENRADIUS, tokeny-TOKENRADIUS, 2*TOKENRADIUS, 2*TOKENRADIUS);
510
                        
511
                        // create an inner circle along which the connections are made.
512
                        // let the diameter of this circle be 2*RADIUS less than the outerDiameter.
513
                        // see what connections exist
514
                        for (int row = 0; row < numBots; row++) {
515
                                for(int col = 0; col < numBots; col++) {
516
                                        if (!entries[row][col].equals("-") && entries[col][row].equals("-") && row != col) {
517
                                                //TODO: Make a standard gray
518
                                                drawConnection(row, col, BIGRADIUS-RADIUS, new Color(200,200,200));
519
                                        } else if (!entries[row][col].equals("-") && ! entries[col][row].equals("-") && row != col) {
520
                                                drawConnection(row, col, BIGRADIUS-RADIUS, Color.BLACK);
521
                                        }
522
                                }
523
                        }
524
                        
525
                        // draw the selection indicator
526
                        indicator.draw();
527
                        
528
                } else {// if matrix is not valid
529
                        this.showStatus("Error: Invalid matrix");
530
                }
531
        
532
        }
533
        
534
        /*        At this point, moveToken is only called by the simulator.
535
        *        In the future, it can be rewritten to account for non-standard
536
        *        token passing or deleted if the information can be retrieved
537
        *        directly from the Colonet server instead.
538
        */
539
        public void moveToken () {
540
                try {
541
                        tokenLoc = (tokenLoc+1)%numBots;
542
                } catch (ArithmeticException e) {  // in case numRobots is zero
543
                }
544
                
545
                packetMonitor.addTokenPass();
546
        }
547
        
548
        public JTextArea getLog () {
549
                return log;
550
        }
551
        
552
        public JTextArea getMatrixInput () {
553
                return txtMatrix;
554
        }
555
        
556
        public void parseMatrix (String line) {
557
                txtMatrix.setText("");
558
                String [] str = line.split(" ");
559
                int num = Integer.parseInt(str[2]);
560
                for (int i = 0; i < num; i++) {
561
                        for (int j = 0; j < num; j++) {
562
                                String next = str[3 + i*num + j];
563
                                if (next.equals("-1"))
564
                                        txtMatrix.append("-");
565
                                else 
566
                                        txtMatrix.append(next);
567
                                if (j < num - 1) 
568
                                        txtMatrix.append(" ");
569
                        }
570
                        if (i < num - 1) 
571
                                txtMatrix.append("\n");
572
                }
573
                
574
        }
575
        
576
        public void parseQueue (String line) {
577
                log.append("Got queue update\n");
578
        
579
        }
580
        
581
        public void parseXBeeIDs (String line) {
582
                String [] str = line.split(" ");
583
                int num = Integer.parseInt(str[2]);
584
                xbeeID = new int[num];
585
                for (int i = 0; i < num; i++)
586
                        xbeeID[i] = Integer.parseInt(str[i+3]);
587
                
588
                //update the list of robots to control
589
                cmbRobotNum.removeAllItems();
590
                cmbRobotNum.addItem(new String("   All   "));
591
                for (int i = 0; i < num; i++)
592
                        cmbRobotNum.addItem(new String("" + xbeeID[i]));
593
        }
594
        
595
        //
596
        // MouseEvent methods
597
        //
598
        public void mouseExited(MouseEvent e) {
599
        }
600
        public void mouseEntered(MouseEvent e) {
601
        }
602
        public void mouseReleased(MouseEvent e) {
603
                vectorController.sendToServer();
604
        }
605
        public void mouseClicked(MouseEvent e) {
606
                mouseDragged(e);
607
        }
608
        public void mousePressed(MouseEvent e) {
609
                try {
610
                        for (int i = 0; i < numBots; i++) {
611
                                if (botRect[i].contains(e.getPoint()))
612
                                        selectedBot = i;
613
                        }
614
                } catch (Exception ex) {
615
                        System.out.println(e);
616
                }
617
        
618
        }
619
        public void mouseDragged(MouseEvent e) {
620
                vectorController.setPoint(e.getX(), e.getY());
621
                vectorController.repaint();
622
        }
623
        public void mouseMoved(MouseEvent e) {
624
        }
625
        
626
        
627
        /*
628
        *        SelectionIndicator thread.
629
        *        Graphical representation of the selection marker
630
        *
631
        *        step() and draw() are synchronized methods. step() is private and 
632
        *        used to update the position of the crosshairs. draw() is called 
633
        *        externally and should only run if all calculations in step() have
634
        *        been completed.
635
        */
636
        private class SelectionIndicator extends Thread {
637
        
638
                final int INDICATOR_DELAY = 100;
639
                final double DTHETA = 0.4;    //larger values make the marker rotate faster
640
                Graphics2D g;   //canvas to draw on
641
                boolean running;
642
                
643
                int sx, sy;                //center
644
                int r, dr;                //radius and width of marker
645
                double theta;   //current angle
646
                
647
                volatile Polygon poly1, poly2, poly3, poly4;
648
                
649
                int px1, py1;
650
                int rx1, ry1;
651
                int px2, py2;
652
                int rx2, ry2;
653
                int px3, py3;
654
                int rx3, ry3;
655
                int px4, py4;
656
                int rx4, ry4;
657
                
658
                int steps;
659
        
660
                public SelectionIndicator (Graphics2D g) {
661
                        super("SelectionIndicator");
662
                        this.g = g;
663
                        running = false;
664
                        steps = 0;
665
                        
666
                        theta = 0;
667
                        rx1 = 0; ry1 = 0;
668
                        px1 = 0; py1 = 0;
669
                        rx2 = 0; ry2 = 0;
670
                        px2 = 0; py2 = 0;
671
                        rx3 = 0; ry3 = 0;
672
                        px3 = 0; py3 = 0;
673
                        rx4 = 0; ry4 = 0;
674
                        px4 = 0; py4 = 0;
675
                }
676
                
677
                public synchronized void setCenter (int sx, int sy) {
678
                        if (sx == this.sx && sy == this.sy) return;
679
                        this.sx = sx;
680
                        this.sy = sy;
681
                        steps = 0;
682
                }
683
                
684
                public synchronized void setRadius (int r, int dr) {
685
                        this.r = r;
686
                        this.dr = dr;
687
                        steps = 0;
688
                }
689
                
690
                public void run () {
691
                        running = true;
692
                        while (running) {
693
                                step();
694
                                try { 
695
                                        Thread.sleep(INDICATOR_DELAY);
696
                                } catch (InterruptedException e) {
697
                                        running = false;
698
                                        return;
699
                                }
700
                        }
701
                }
702
                
703
                private synchronized void step () {
704
                        Polygon poly1_new = new Polygon();
705
                        Polygon poly2_new = new Polygon();
706
                        Polygon poly3_new = new Polygon();
707
                        Polygon poly4_new = new Polygon();
708
                
709
                        //the step
710
                        theta = (theta + DTHETA/Math.PI) % (Math.PI);
711
                        
712
                        //the calculation
713
                        //let p be the point of the pointy thing toward the center
714
                        //let r be the point at the opposite side
715
                        
716
                        //recalculate radius, if it will look cool, lolz
717
                        int newr = r;
718
                        if (steps < 100)
719
                        newr = (int)( r + 200/(steps+1) );
720
                        
721
                        //precompute values for dx and dy
722
                        int dx_inner = (int)(newr * Math.cos(theta));
723
                        int dy_inner = (int)(newr * Math.sin(theta));
724
                        int dx_outer = (int)((newr+dr) * Math.cos(theta));
725
                        int dy_outer = (int)((newr+dr) * Math.sin(theta));
726
                        
727
                        //calculate polygon constants
728
                        int dy_poly = (int)(dr/2 * Math.cos(theta));
729
                        int dx_poly = (int)(dr/2 * Math.sin(theta));
730
                        
731
                        //determine critical points
732
                        //kansas city shuffle!
733
                        px1 = sx + dx_inner;
734
                        py1 = sy - dy_inner;
735
                        rx1 = sx + dx_outer;
736
                        ry1 = sy - dy_outer;
737
                        px2 = sx - dx_inner;
738
                        py2 = sy + dy_inner;
739
                        rx2 = sx - dx_outer;
740
                        ry2 = sy + dy_outer;
741
                        px3 = sx - dy_inner;
742
                        py3 = sy - dx_inner;
743
                        rx3 = sx - dy_outer;
744
                        ry3 = sy - dx_outer;
745
                        px4 = sx + dy_inner;
746
                        py4 = sy + dx_inner;
747
                        rx4 = sx + dy_outer;
748
                        ry4 = sy + dx_outer;
749
                        
750
                        //create polygons
751
                        poly1_new.addPoint(px1, py1);
752
                        poly1_new.addPoint(rx1+dx_poly, ry1+dy_poly);
753
                        poly1_new.addPoint(rx1-dx_poly, ry1-dy_poly);
754
                        poly2_new.addPoint(px2, py2);
755
                        poly2_new.addPoint(rx2+dx_poly, ry2+dy_poly);
756
                        poly2_new.addPoint(rx2-dx_poly, ry2-dy_poly);
757
                        poly3_new.addPoint(px3, py3);
758
                        poly3_new.addPoint(rx3-dy_poly, ry3+dx_poly);
759
                        poly3_new.addPoint(rx3+dy_poly, ry3-dx_poly);
760
                        poly4_new.addPoint(px4, py4);
761
                        poly4_new.addPoint(rx4-dy_poly, ry4+dx_poly);
762
                        poly4_new.addPoint(rx4+dy_poly, ry4-dx_poly);
763
                        
764
                        //reassign updated polygons
765
                        poly1 = poly1_new;
766
                        poly2 = poly2_new;
767
                        poly3 = poly3_new;
768
                        poly4 = poly4_new;
769
                
770
                        if (steps < 300) steps++;
771
                }
772
                
773
                public synchronized void draw () {
774
                        if (!running) return;
775
                        g.setColor(Color.GRAY);
776
                        //draw polygons
777
                        g.fillPolygon(poly1);
778
                        g.fillPolygon(poly2);
779
                        g.fillPolygon(poly3);
780
                        g.fillPolygon(poly4);
781
                }
782
        
783
        }
784
        
785
        /*
786
        *        Simulator thread.
787
        *
788
        */
789
        private class Simulator extends Thread {
790
                final int SIMULATOR_DELAY = 300;
791
                boolean running;
792
        
793
                public Simulator () {
794
                        super("Simulator");
795
                        running = false;
796
                }
797
                
798
                public void run () {
799
                        running = true;
800
                        while (running) {
801
                                step();
802
                                try { 
803
                                        Thread.sleep(SIMULATOR_DELAY);
804
                                } catch (InterruptedException e) {
805
                                        running = false;
806
                                        return; 
807
                                }
808
                        }
809
                }
810
                
811
                private void step () {
812
                        // don't do anything! the colonet should work on its own!
813
                }
814
        
815
        }
816
        
817
        /*
818
        *        PacketMonitor thread.
819
        *
820
        *        Currently, this counts the rate of token passes but will eventually 
821
        *        be modified to keep more important statistics.
822
        */
823
        private class PacketMonitor extends Thread {
824
                final int PACKETMONITOR_DELAY = 1000;
825
        
826
                boolean running;
827
                int tokenPasses;
828
        
829
                public PacketMonitor () {
830
                        super("PacketMonitor");
831
                        running = false;
832
                        tokenPasses = 0;
833
                }
834
                
835
                public void run () {
836
                        running = true;
837
                        while (running) {
838
                                displayTokenPasses();
839
                                try { 
840
                                        Thread.sleep(PACKETMONITOR_DELAY);
841
                                } catch (InterruptedException e) { 
842
                                        running = false;
843
                                        return; 
844
                                }
845
                        }
846
                }
847
                
848
                public synchronized void addTokenPass () {
849
                        tokenPasses++;
850
                }
851
                
852
                public synchronized void displayTokenPasses () {
853
                        lblTokenPasses.setText("" + tokenPasses);
854
                        tokenPasses = 0;
855
                }
856
        
857
        }
858
        
859
        /*
860
        *        DataUpdater thread.
861
        *   The purpose of this thread is to request data from the server at regular intervals.
862
        *
863
        */
864
        class DataUpdater extends Thread {
865
                final int DATAUPDATER_DELAY = 5000;
866
                
867
                public DataUpdater () {
868
                        super("Colonet DataUpdater");
869
                }
870
                
871
                public void run () {
872
                        String line;
873
                        while (true) {
874
                                try {
875
                                        //request more data
876
                                        if (csi.isReady()) {
877
                                                csi.sendSensorDataRequest();
878
                                                csi.sendXBeeIDRequest();
879
                                        }
880
                                        Thread.sleep(DATAUPDATER_DELAY);
881
                                } catch (InterruptedException e) {
882
                                        return;
883
                                } 
884
                        }
885
                }
886

    
887
        }
888
        
889
        /*
890
        *        ImagePanel class
891
        *        Enables more efficient image handling in a component-controlled environment
892
        */
893
        class ImagePanel extends JPanel {
894
                protected Image img;
895
        
896
                public ImagePanel (Image img) {
897
                        super();
898
                        this.img = img;
899
                }
900
                
901
                public ImagePanel (boolean isDoubleBuffered, Image img) {
902
                        super(isDoubleBuffered);
903
                        this.img = img;
904
                }
905
                
906
                public void paint (Graphics g) {
907
                        // Place the buffered image on the screen, inside the panel
908
                        g.drawImage(img, 0, 0, Color.WHITE, this);
909
                }
910
        
911
        }
912
        
913
        /*
914
        *        VectorController class
915
        *        Manages robot motion control graphically
916
        */
917
        class VectorController extends ImagePanel {
918
                int x, y, cx, cy;
919
                int width, height;
920
                int side;
921
                
922
                public VectorController (Image img) {
923
                        super (img);
924
                        width = img.getWidth(null);
925
                        height = img.getHeight(null);
926
                        cx = img.getWidth(null)/2;
927
                        cy = img.getHeight(null)/2;
928
                        x = cx;
929
                        y = cy;
930
                        if (width < height)
931
                                side = width;
932
                        else
933
                                side = height;
934
                }
935
                
936
                public void setPoint (int x, int y) {
937
                        if (!isValidPoint(x, y))
938
                                return;
939
                        this.x = x;
940
                        this.y = y;
941
                        repaint();
942
                }
943
                
944
                public boolean isValidPoint (int x, int y) {
945
                        double xterm = Math.pow(1.0*(x - cx)/(side/2), 2);
946
                        double yterm = Math.pow(1.0*(y - cy)/(side/2), 2);
947
                        return (xterm + yterm <= 1);
948
                }
949
                
950
                public int getVelocity () {
951
                        int dx = x - cx;
952
                        int dy = y - cy;
953
                        int v = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) );
954
                        return v;
955
                }
956
                
957
                public int getAngle () {
958
                        int dx = x - cx;
959
                        int dy = cy - y;
960
                        double theta = Math.atan2(Math.abs(dx), Math.abs(dy));
961
                        theta = (int) (1.0 * theta * 180 / Math.PI);  //transform to degrees
962
                        if (dy < 0)
963
                                theta = 180 - theta;
964
                        theta *= Math.signum(dx);
965
                        return (int) theta;
966
                }
967
                
968
                public void paint (Graphics g) {
969
                        g.setColor(Color.BLACK);
970
                        g.fillRect(0, 0, width, height);
971
                        ((Graphics2D)g).setStroke(new BasicStroke(1));
972
                        g.setColor(Color.RED);
973
                        g.drawOval(cx-side/2, cy-side/2, side, side);
974
                        ((Graphics2D)g).setStroke(new BasicStroke(2));
975
                        g.setColor(Color.GREEN);
976
                        g.drawLine(cx, cy, x, y);
977
                        g.fillOval(x-3, y-3, 6, 6);
978
                }
979
                
980
                public void setMaxForward () {
981
                        setPoint(cx, cy - (side/2) + 1);
982
                }
983
                
984
                public void setMaxReverse () {
985
                        setPoint(cx, cy + (side/2) - 1);
986
                }
987
                
988
                public void setMaxLeft () {
989
                        setPoint(cx - (side/2) + 1, cy);
990
                }
991
                
992
                public void setMaxRight () {
993
                        setPoint(cx + (side/2) - 1, cy);
994
                }
995
                
996
                public void setZero () {
997
                        setPoint(cx, cy);
998
                }
999
                
1000
                public void sendToServer () {
1001
                        if (csi != null)
1002
                                csi.sendData(ColonetServerInterface.MOVE + " " + 
1003
                                        vectorController.getAngle() + " " + 
1004
                                        vectorController.getVelocity(), ColonetServerInterface.GLOBAL_DEST);
1005
                }
1006
        
1007
        }
1008
        
1009
        /*
1010
        *        TaskAddWindow class
1011
        *        makes it easy to add tasks to the queue
1012
        */
1013
        class TaskAddWindow extends JFrame implements ActionListener, ListSelectionListener {
1014
                JPanel panelButtons;
1015
                JPanel panelParameters;
1016
                JPanel panelSouth;
1017
                JPanel panelSelection;
1018
                JButton btnSubmit;
1019
                JButton btnCancel;
1020
                DefaultListModel availableListModel;
1021
                JList availableList;
1022
                JScrollPane spAvailableTasks;
1023
                JTextArea txtDescription;
1024
                JTextField txtParameters;
1025
                MouseListener mouseListener;
1026
                
1027
                public TaskAddWindow () {
1028
                        super("Add a Task ... if you dare!");
1029
                        super.setSize(500,500);
1030
                        super.setLayout(new BorderLayout());
1031
                        
1032
                        // set up buttons
1033
                        btnSubmit = new JButton("Submit");
1034
                        btnCancel = new JButton("Cancel");
1035
                        panelButtons = new JPanel();
1036
                        panelButtons.setLayout(new FlowLayout());
1037
                        panelButtons.add(btnSubmit);
1038
                        panelButtons.add(btnCancel);
1039
                        this.getRootPane().setDefaultButton(btnSubmit);
1040
                        
1041
                        // set up task list
1042
                        availableListModel = new DefaultListModel();
1043
                        availableListModel.addElement("Map the Environment");
1044
                        availableListModel.addElement("Clean Up Chemical Spill");
1045
                        availableListModel.addElement("Grow Plants");
1046
                        availableListModel.addElement("Save the Cheerleader");
1047
                        availableListModel.addElement("Save the World");
1048
                        availableList = new JList(availableListModel);
1049
                        availableList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1050
                        availableList.setSelectedIndex(-1);
1051
                        spAvailableTasks = new JScrollPane(availableList);
1052
                        spAvailableTasks.setBorder(BorderFactory.createTitledBorder("Select A Task"));
1053
                        txtDescription = new JTextArea();
1054
                        txtDescription.setEditable(false);
1055
                        txtDescription.setLineWrap(true);
1056
                        txtDescription.setWrapStyleWord(true);
1057
                        txtDescription.setBorder(BorderFactory.createTitledBorder("Description"));
1058
                        
1059
                        //set up parameter area
1060
                        panelParameters = new JPanel();
1061
                        panelParameters.setLayout(new BorderLayout());
1062
                        txtParameters = new JTextField();
1063
                        panelParameters.add(new JLabel("Optional parameters for this task: "), BorderLayout.WEST);
1064
                        panelParameters.add(txtParameters);
1065
                        
1066
                        // assemble objects
1067
                        panelSelection = new JPanel();
1068
                        panelSelection.setLayout(new GridLayout(1,2));
1069
                        panelSelection.add(spAvailableTasks);
1070
                        panelSelection.add(txtDescription);
1071
                        
1072
                        panelSouth = new JPanel();
1073
                        panelSouth.setLayout(new GridLayout(2,1));
1074
                        panelSouth.add(panelParameters);
1075
                        panelSouth.add(panelButtons);
1076
                        
1077
                        this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
1078
                        this.getContentPane().add(panelSelection, BorderLayout.CENTER);
1079
                        this.setLocationRelativeTo(null);
1080
                        
1081
                        // add listeners here
1082
                        availableList.addListSelectionListener(this);
1083
                        btnSubmit.addActionListener(this);
1084
                        btnCancel.addActionListener(this);
1085
                }
1086
                
1087
                public void prompt () {
1088
                        this.setVisible(true);
1089
                }
1090
                
1091
                private String getDescription (int index) {
1092
                        if (index < 0)
1093
                                return "";
1094
                        switch (index) {
1095
                                case 0: return "SLAM and junk";
1096
                                case 1: return "I'm not sure this works";
1097
                                case 2: return "Push them into the light";
1098
                                case 3: return "Watch out for clock repair guys";
1099
                                case 4: return "But make sure he's dead, geez, why would you let him get away? I mean come on, "
1100
                                        +"he's just lying there and everyone's too busy looking at people flying through the sky to "
1101
                                        +"notice? Oh yeah, that's a good transition to a new season, let's make the audience think "
1102
                                        +"he's dead and then pull a fast one on them, they'll never see that coming.";
1103
                        
1104
                                default: return "Task not recognized";
1105
                        }
1106
                }
1107
                
1108
                public void actionPerformed (ActionEvent e) {
1109
                        Object source = e.getSource();
1110
                        if (source == btnSubmit) {
1111
                                txtParameters.setText(txtParameters.getText().trim());
1112
                                
1113
                                
1114
                                this.setVisible(false);
1115
                        } else if (source == btnCancel) {
1116
                                this.setVisible(false);
1117
                        }
1118
                }
1119
                
1120
                public void valueChanged (ListSelectionEvent e) {
1121
                        int index = availableList.getSelectedIndex();
1122
                        if (index >= 0)
1123
                                txtDescription.setText(getDescription(index));
1124
                }
1125
        
1126
        }
1127

    
1128
}