Project

General

Profile

Statistics
| Revision:

root / branches / lib_additions / code / projects / colonet / ColonetGUI / Colonet.java @ 79

History | View | Annotate | Download (23.1 KB)

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

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

    
14
public class Colonet extends JApplet implements ActionListener, MouseListener, Runnable {
15

    
16
        final int CANVAS_SIZE = 500;  //don't make this too large, or the applet will be slow.
17
        final int BUFFER = 50;
18
        final int RADIUS = 30;
19

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

    
87
        
88
        public void init () {
89
                // set the default look and feel
90
        String laf = UIManager.getSystemLookAndFeelClassName();
91
        try {
92
            UIManager.setLookAndFeel(laf);
93
        } catch (UnsupportedLookAndFeelException exc) {
94
            System.err.println ("Warning: UnsupportedLookAndFeel: " + laf);
95
        } catch (Exception exc) {
96
            System.err.println ("Error loading " + laf + ": " + exc);
97
        }
98
                // We should invoke and wait to avoid browser display difficulties
99
                Runnable r = new Runnable() {
100
                        public void run() {
101
                                createAndShowGUI();
102
                        }
103
                };
104
                try {
105
                        SwingUtilities.invokeAndWait(r);
106
                } catch (InterruptedException e) {
107
                        //Not really sure why we would be in this situation
108
                        System.out.println(e);
109
                } catch (java.lang.reflect.InvocationTargetException e) {
110
                        //This should never happen. Seriously.
111
                        System.out.println(e);
112
                }
113
        }
114
        
115
        public void destroy () {
116
                try { drawThread.interrupt(); } catch (Exception e) { }
117
                try { indicator.interrupt(); } catch (Exception e) { }
118
                try { packetMonitor.interrupt(); } catch (Exception e) { }
119
        }
120

    
121
        private synchronized void createAndShowGUI () {
122
                // init graphical elements
123
                panel = new JPanel(false);  //set automatic double-buffering to false. we are doing it manually.
124
                
125
                // Connection area
126
                txtMatrix = new JTextArea("- 9 3 - 1\n- - - 5 -\n4 - - - 2\n- - - - -\n1 - - 3 -");
127
                txtMatrix.setBorder(BorderFactory.createTitledBorder("Input Matrix"));
128
                txtInfo = new JTextArea();
129
                txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
130
                txtInfo.setEditable(false);
131
                btnGraph = new JButton("Run");
132
                txtHost = new JTextField("roboclub1.frc.ri.cmu.edu");
133
                txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
134
                txtPort = new JTextField("10123");
135
                txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
136
                btnConnect = new JButton("Connect");
137
                lblConnectionStatus = new JLabel("Status: Offline");
138
                panelConnect = new JPanel();
139
                panelConnect.setLayout(new GridLayout(6,1));
140
                panelConnect.add(lblConnectionStatus);
141
                panelConnect.add(txtHost);
142
                panelConnect.add(txtPort);
143
                panelConnect.add(btnConnect);
144
                panelConnect.add(txtInfo);
145
                panelConnect.add(btnGraph);
146
                panelServerInterface = new JPanel();
147
                panelServerInterface.setLayout(new GridLayout(2,1));
148
                panelServerInterface.add(panelConnect);
149
                panelServerInterface.add(txtMatrix);
150
                                
151
                // Status Elements
152
                lblTokenPasses = new JLabel();
153
                lblBattery = new JLabel("???");
154
                panelStats = new JPanel();
155
                panelStats.setLayout(new GridLayout(4,2));
156
                panelStats.add(new JLabel("Token Passes / sec          "));
157
                panelStats.add(lblTokenPasses);
158
                panelStats.add(new JLabel("Battery     "));
159
                panelStats.add(lblBattery);
160
                panelStats.add(new JLabel("Token Passes / sec     "));
161
                panelStats.add(lblTokenPasses);
162
                
163
                //TODO: add panelStats somewhere?
164

    
165
                // Robot direction panel
166
                panelRobotDirection = new JPanel();
167
                btnF = new JButton("^");
168
                btnB = new JButton("v");
169
                btnL = new JButton("<");
170
                btnR = new JButton(">");
171
                btnActivate = new JButton("o");
172
                panelRobotDirection = new JPanel();
173
                panelRobotDirection.setLayout(new GridLayout(3,3));
174
                panelRobotDirection.add(new JLabel(""));
175
                panelRobotDirection.add(btnF);
176
                panelRobotDirection.add(new JLabel(""));
177
                panelRobotDirection.add(btnL);
178
                panelRobotDirection.add(btnActivate);
179
                panelRobotDirection.add(btnR);
180
                panelRobotDirection.add(new JLabel(""));
181
                panelRobotDirection.add(btnB);
182
                panelRobotDirection.add(new JLabel(""));
183
                
184
                // Robot Control and Commands
185
                panelRobotCommands = new JPanel();
186
                panelRobotCommands.setLayout(new FlowLayout());
187
                cmbRobotNum = new JComboBox();
188
                panelRobotCommands.add(cmbRobotNum);
189
                panelRobotCommands.add(new JLabel("Commands go here"));
190
                panelRobotControl = new JPanel();
191
                panelRobotControl.setLayout(new GridLayout(2,1));
192
                panelRobotControl.add(panelRobotDirection);
193
                panelRobotControl.add(panelRobotCommands);
194
                
195
                // Task Manager
196
                panelTaskManager = new JPanel();
197
                panelTaskManager.setLayout(new BorderLayout());
198
                taskListModel = new DefaultListModel();
199
                taskListModel.addElement("Map the Environment");
200
                taskListModel.addElement("Clean Up Chemical Spill");
201
                taskListModel.addElement("Grow Plants");
202
                taskListModel.addElement("Save the Cheerleader");
203
                taskListModel.addElement("Save the World");
204
                taskList = new JList(taskListModel);
205
                taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
206
                taskList.setSelectedIndex(0);
207
                spTaskManager = new JScrollPane(taskList);
208
                panelTaskManagerControls = new JPanel();
209
                panelTaskManagerControls.setLayout(new GridLayout(1,3));
210
                panelTaskManagerControlsPriority = new JPanel();
211
                panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
212
                btnAddTask = new JButton("Add...");
213
                btnRemoveTask = new JButton("Remove");
214
                btnMoveTaskUp = new JButton("^");
215
                btnMoveTaskDown = new JButton("v");
216
                panelTaskManagerControlsPriority.add(btnMoveTaskUp);
217
                panelTaskManagerControlsPriority.add(btnMoveTaskDown);
218
                panelTaskManagerControls.add(btnAddTask);
219
                panelTaskManagerControls.add(btnRemoveTask);
220
                panelTaskManagerControls.add(panelTaskManagerControlsPriority);
221
                panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
222
                panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
223
                panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
224
                
225
                // Message log
226
                log = new JTextArea();
227
                spLog = new JScrollPane(log,
228
                        ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 
229
                        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
230
                spLog.setBorder(BorderFactory.createTitledBorder("Log"));
231
                spLog.setPreferredSize(new Dimension(0, 150));
232
                log.setEditable(false);
233
                
234
                // Main control mechanism
235
                panelControl = new JPanel();
236
                panelControl.setLayout(new GridLayout(1,1));
237
                tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
238
                tabPaneControl.setPreferredSize(new Dimension(300, 0));
239
                tabPaneControl.addTab("Connection", panelServerInterface);
240
                tabPaneControl.addTab("Robots", panelRobotControl);
241
                tabPaneControl.addTab("Tasks", panelTaskManager);
242
                panelControl.add(tabPaneControl);
243
                
244
                // Set up elements in the south
245
                panelSouth = new JPanel();
246
                panelSouth.setLayout(new GridLayout(1,2));
247
                panelSouth.add(spLog);
248

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

    
806
        }
807

    
808
}