Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (21.6 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 txtInput;
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
        
50
        // Task Manager
51
        JPanel panelTaskManager;
52
        JScrollPane spTaskManager;
53
        JPanel panelTaskManagerControls;
54
        JPanel panelTaskManagerControlsPriority;
55
        DefaultListModel taskListModel;
56
        JList taskList;
57
        JButton btnAddTask;
58
        JButton btnRemoveTask;
59
        JButton btnMoveTaskUp;
60
        JButton btnMoveTaskDown;
61
        
62
        // Graphics
63
        JPanel panel;
64
        GraphicsConfiguration gc;
65
        volatile BufferedImage image;
66
        volatile Graphics2D canvas;
67
        int cx, cy;
68
        
69
        Socket socket;                                        
70
        OutputStreamWriter out;                        //TODO: add a BufferedWriter
71
        DataListener datalistener;  
72
        
73
        Font botFont;
74
        Random random = new Random();
75
        volatile int tokenLoc;  //the token is currently here
76
        volatile int numBots;
77
        volatile int selectedBot;  //the user has selected this bot
78
        volatile Rectangle[] botRect;  //contains boundary shapes around bots for click detection
79
        
80
        Thread drawThread;
81
        SelectionIndicator indicator;
82
        PacketMonitor packetMonitor;
83
        ColonetServerInterface csi;
84

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

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

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

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

    
778
        }
779

    
780
}