Project

General

Profile

Statistics
| Revision:

root / branches / autonomous_recharging / code / projects / colonet / ColonetGUI / Colonet.java @ 956

History | View | Annotate | Download (23.1 KB)

1 32 gtress
//
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 76 gtress
        JTextArea txtMatrix;
27 32 gtress
        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 67 gtress
        JComboBox cmbRobotNum;
50 32 gtress
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 76 gtress
        DataUpdater dataUpdater;
73 32 gtress
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 76 gtress
        Simulator simulator;
83 32 gtress
        SelectionIndicator indicator;
84
        PacketMonitor packetMonitor;
85 35 gtress
        ColonetServerInterface csi;
86 32 gtress
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 76 gtress
                txtMatrix = new JTextArea("- 9 3 - 1\n- - - 5 -\n4 - - - 2\n- - - - -\n1 - - 3 -");
127
                txtMatrix.setBorder(BorderFactory.createTitledBorder("Input Matrix"));
128 32 gtress
                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 76 gtress
                panelServerInterface.add(txtMatrix);
150 32 gtress
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 72 gtress
                //TODO: add panelStats somewhere?
164 32 gtress
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 67 gtress
                cmbRobotNum = new JComboBox();
188
                panelRobotCommands.add(cmbRobotNum);
189 32 gtress
                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 72 gtress
                /* Add all listeners here */
256
                // Task Management
257 32 gtress
                btnAddTask.addActionListener(this);
258
                btnRemoveTask.addActionListener(this);
259
                btnMoveTaskUp.addActionListener(this);
260
                btnMoveTaskDown.addActionListener(this);
261 72 gtress
                // 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 32 gtress
                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 76 gtress
                simulator = new Simulator();
294 35 gtress
295 76 gtress
                csi = new ColonetServerInterface(log, txtMatrix);
296 32 gtress
297
        }
298
299 35 gtress
        public synchronized void paint (Graphics g) {
300 32 gtress
                /*        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 35 gtress
        public synchronized void update (Graphics g) {
312 32 gtress
                paint(g);
313
        }
314
315 35 gtress
        public void actionPerformed (ActionEvent e) {
316 32 gtress
                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 76 gtress
                        simulator.start();
327 32 gtress
                } else if (source == btnConnect) {
328
                        doSocket();
329 76 gtress
                        dataUpdater = new DataUpdater();
330
                        dataUpdater.start();
331 32 gtress
                } else if (source == btnRemoveTask) {
332
                        try {
333
                                taskListModel.remove(taskList.getSelectedIndex());
334
                        } catch (ArrayIndexOutOfBoundsException ex) {
335
                        }
336 72 gtress
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 32 gtress
                }
354
        }
355
356 35 gtress
        private void randomize () {
357 32 gtress
                Random r = new Random();
358
                StringBuilder s = new StringBuilder();
359
360
                int count = r.nextInt(8) + 1;
361 35 gtress
                for (int i = 0; i < count; i++) {
362
                        for (int j = 0; j < count; j++) {
363 32 gtress
                                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 76 gtress
                txtMatrix.setText(s.toString());
374 32 gtress
        }
375
376 35 gtress
        private void doSocket () {
377
                csi.connect(txtHost.getText(), txtPort.getText());
378 32 gtress
        }
379
380 35 gtress
        public void drawRobot (int id, int x, int y) {
381 32 gtress
                //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 35 gtress
        public void drawConnection (int start, int end, int radius, Color color) {
394 32 gtress
                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 35 gtress
        public void run () {
436
                while (true) {
437 32 gtress
                        step();
438
                        repaint();
439 35 gtress
                        try {
440
                                Thread.sleep(90);
441
                        } catch (InterruptedException e) {
442
                                return;
443
                        }
444 32 gtress
                }
445
        }
446
447 35 gtress
        public void step () {
448 32 gtress
                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 76 gtress
                String [] rows = txtMatrix.getText().split("\n");
459 32 gtress
                numBots = rows.length;
460
                String [][] entries = new String[numBots][numBots];
461
                valid = true;
462 35 gtress
                for (int i = 0; i < numBots; i++) {
463 32 gtress
                        entries[i] = rows[i].split(" ");
464
                        if (entries[i].length != rows.length) valid = false;
465
                }
466
467 35 gtress
                if (valid) {
468 38 gtress
                        this.showStatus("Running");
469 32 gtress
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 35 gtress
                        for (int i = 0; i < numBots; i++) {
477 32 gtress
                                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 35 gtress
                        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 32 gtress
                                                drawConnection(row, col, BIGRADIUS-RADIUS, new Color(200,200,200));
499 38 gtress
                                        } else if (!entries[row][col].equals("-") && ! entries[col][row].equals("-") && row != col) {
500 32 gtress
                                                drawConnection(row, col, BIGRADIUS-RADIUS, Color.BLACK);
501
                                        }
502
                                }
503
                        }
504
505
                        // draw the selection indicator
506
                        indicator.draw();
507
508 35 gtress
                } else {// if matrix is not valid
509 32 gtress
                        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 35 gtress
        public void moveToken () {
520
                try {
521
                        tokenLoc = (tokenLoc+1)%numBots;
522
                } catch (ArithmeticException e) {  // in case numRobots is zero
523
                }
524 32 gtress
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 35 gtress
        public void mousePressed(MouseEvent e) {
536 32 gtress
                try {
537 35 gtress
                        for (int i = 0; i < numBots; i++) {
538 32 gtress
                                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 35 gtress
        private class SelectionIndicator extends Thread {
557 32 gtress
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 35 gtress
                public SelectionIndicator (Graphics2D g) {
581 32 gtress
                        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 35 gtress
                public synchronized void setCenter (int sx, int sy) {
598 32 gtress
                        if (sx == this.sx && sy == this.sy) return;
599
                        this.sx = sx;
600
                        this.sy = sy;
601
                        steps = 0;
602
                }
603
604 35 gtress
                public synchronized void setRadius (int r, int dr) {
605 32 gtress
                        this.r = r;
606
                        this.dr = dr;
607
                        steps = 0;
608
                }
609
610 35 gtress
                public void run () {
611 32 gtress
                        running = true;
612 35 gtress
                        while (running) {
613 32 gtress
                                step();
614 35 gtress
                                try {
615
                                        Thread.sleep(INDICATOR_DELAY);
616
                                } catch (InterruptedException e) {
617
                                        running = false;
618
                                        return;
619
                                }
620 32 gtress
                        }
621
                }
622
623 35 gtress
                private synchronized void step () {
624 32 gtress
                        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 38 gtress
                        if (steps < 100)
639
        newr = (int)( r + 200/(steps+1) );
640 32 gtress
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 35 gtress
                        //kansas city shuffle!
653 32 gtress
                        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 35 gtress
                public synchronized void draw () {
694 32 gtress
                        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 35 gtress
        private class Simulator extends Thread {
710 32 gtress
                final int SIMULATOR_DELAY = 300;
711
                boolean running;
712
713 35 gtress
                public Simulator () {
714 32 gtress
                        super("Simulator");
715
                        running = false;
716
                }
717
718 35 gtress
                public void run () {
719 32 gtress
                        running = true;
720 35 gtress
                        while (running) {
721 32 gtress
                                step();
722 35 gtress
                                try {
723
                                        Thread.sleep(SIMULATOR_DELAY);
724
                                } catch (InterruptedException e) {
725
                                        running = false;
726
                                        return;
727
                                }
728 32 gtress
                        }
729
                }
730
731 35 gtress
                private void step () {
732 32 gtress
                        // 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 35 gtress
        private class PacketMonitor extends Thread {
745 32 gtress
                final int PACKETMONITOR_DELAY = 1000;
746
747
                boolean running;
748
                int tokenPasses;
749
750 35 gtress
                public PacketMonitor () {
751 32 gtress
                        super("PacketMonitor");
752
                        running = false;
753
                        tokenPasses = 0;
754
                }
755
756 35 gtress
                public void run () {
757 32 gtress
                        running = true;
758 35 gtress
                        while (running) {
759 32 gtress
                                displayTokenPasses();
760 35 gtress
                                try {
761
                                        Thread.sleep(PACKETMONITOR_DELAY);
762
                                } catch (InterruptedException e) {
763
                                        running = false;
764
                                        return;
765
                                }
766 32 gtress
                        }
767
                }
768
769 35 gtress
                public synchronized void addTokenPass () {
770 32 gtress
                        tokenPasses++;
771
                }
772
773 35 gtress
                public synchronized void displayTokenPasses () {
774 32 gtress
                        lblTokenPasses.setText("" + tokenPasses);
775
                        tokenPasses = 0;
776
                }
777
778
        }
779
780
        /*
781 67 gtress
        *        DataUpdater thread.
782 76 gtress
        *   The purpose of this thread is to request data from the server at regular intervals.
783 32 gtress
        *
784
        */
785 67 gtress
        class DataUpdater extends Thread {
786 76 gtress
                final int DATAUPDATER_DELAY = 1100;
787 32 gtress
788 67 gtress
                public DataUpdater () {
789
                        super("Colonet DataUpdater");
790 32 gtress
                }
791
792 35 gtress
                public void run () {
793 32 gtress
                        String line;
794 35 gtress
                        while (true) {
795 32 gtress
                                try {
796 76 gtress
                                        //request more data
797
                                        if (csi.isReady())
798
                                                csi.sendRequest(ColonetServerInterface.REQUEST_BOM_MATRIX, "");
799 67 gtress
                                        Thread.sleep(DATAUPDATER_DELAY);
800 32 gtress
                                } catch (InterruptedException e) {
801
                                        return;
802 39 gtress
                                }
803 32 gtress
                        }
804
                }
805
806
        }
807
808
}