Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (32.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
        JButton btnCommand_StopTask;
55
        JButton btnCommand_ResumeTask;
56
        JButton btnCommand_ChargeNow;
57
        JButton btnCommand_StopCharging;
58
        
59
        // Task Manager
60
        JPanel panelTaskManager;
61
        JScrollPane spTaskManager;
62
        JPanel panelTaskManagerControls;
63
        JPanel panelTaskManagerControlsPriority;
64
        DefaultListModel taskListModel;
65
        JList taskList;
66
        JButton btnAddTask;
67
        JButton btnRemoveTask;
68
        JButton btnMoveTaskUp;
69
        JButton btnMoveTaskDown;
70
        JButton btnUpdateTasks;
71
        TaskAddWindow taskAddWindow;
72
        
73
        // Graphics
74
        ImagePanel panel;
75
        GraphicsConfiguration gc;
76
        volatile BufferedImage image;
77
        volatile Graphics2D canvas;
78
        int cx, cy;
79
        
80
        Socket socket;                                        
81
        OutputStreamWriter out;
82
        DataUpdater dataUpdater;  
83
        
84
        Font botFont;
85
        Random random = new Random();
86
        volatile int tokenLoc;  //the token is currently here
87
        volatile int numBots;
88
        volatile int selectedBot;  //the user has selected this bot
89
        volatile Rectangle[] botRect;  //contains boundary shapes around bots for click detection
90
        volatile int[] xbeeID;
91
        
92
        Thread drawThread;
93
        Simulator simulator;
94
        SelectionIndicator indicator;
95
        PacketMonitor packetMonitor;
96
        ColonetServerInterface csi;
97

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

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

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

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

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

    
1154
}