Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (31.3 KB)

1 32 gtress
//
2
//  Colonet.java
3
//
4
5
import javax.swing.*;
6 136 gtress
import javax.swing.event.*;
7 32 gtress
import java.awt.*;
8
import java.awt.image.*;
9
import java.awt.event.*;
10
import java.net.*;
11
import java.io.*;
12
import java.util.Random;
13
import java.applet.*;
14
15 136 gtress
public class Colonet extends JApplet implements ActionListener, MouseInputListener, Runnable {
16 32 gtress
17 155 gtress
        final int CANVAS_SIZE = 500;  //the applet may be slow if the canvas gets too large
18 32 gtress
        final int BUFFER = 50;
19
        final int RADIUS = 30;
20
21
        // Connection
22
        JTextField txtHost;
23
        JTextField txtPort;
24
        JButton btnConnect;
25
        JButton btnGraph;
26
        JLabel lblConnectionStatus;
27 76 gtress
        JTextArea txtMatrix;
28 32 gtress
        JTextArea txtInfo;
29
        JPanel panelConnect;
30
        JPanel panelServerInterface;
31
32
        // Stats
33
        JLabel lblBattery;
34
        JLabel lblTokenPasses;
35
        JLabel lblHastToken;
36
        JPanel panelStats;
37
38
        // South
39
        JPanel panelSouth;
40
        JTextArea log;
41
        JScrollPane spLog;
42
43
        // Control
44
        JPanel panelControl;
45
        JTabbedPane tabPaneControl;
46
        JPanel panelRobotControl;
47
        JPanel panelRobotDirection;
48 136 gtress
        JPanel panelRobotDirectionButtons;
49 32 gtress
        JPanel panelRobotCommands;
50
        JButton btnF, btnB, btnL, btnR, btnActivate;
51 67 gtress
        JComboBox cmbRobotNum;
52 136 gtress
        VectorController vectorController;
53
        BufferedImage imageVectorControl;
54 168 gtress
        JButton btnCommand_StopTask;
55
        JButton btnCommand_ResumeTask;
56 32 gtress
57
        // Task Manager
58
        JPanel panelTaskManager;
59
        JScrollPane spTaskManager;
60
        JPanel panelTaskManagerControls;
61
        JPanel panelTaskManagerControlsPriority;
62
        DefaultListModel taskListModel;
63
        JList taskList;
64
        JButton btnAddTask;
65
        JButton btnRemoveTask;
66
        JButton btnMoveTaskUp;
67
        JButton btnMoveTaskDown;
68 136 gtress
        JButton btnUpdateTasks;
69
        TaskAddWindow taskAddWindow;
70 32 gtress
71
        // Graphics
72 136 gtress
        ImagePanel panel;
73 32 gtress
        GraphicsConfiguration gc;
74
        volatile BufferedImage image;
75
        volatile Graphics2D canvas;
76
        int cx, cy;
77
78
        Socket socket;
79 136 gtress
        OutputStreamWriter out;
80 76 gtress
        DataUpdater dataUpdater;
81 32 gtress
82
        Font botFont;
83
        Random random = new Random();
84
        volatile int tokenLoc;  //the token is currently here
85
        volatile int numBots;
86
        volatile int selectedBot;  //the user has selected this bot
87
        volatile Rectangle[] botRect;  //contains boundary shapes around bots for click detection
88 136 gtress
        volatile int[] xbeeID;
89 32 gtress
90
        Thread drawThread;
91 76 gtress
        Simulator simulator;
92 32 gtress
        SelectionIndicator indicator;
93
        PacketMonitor packetMonitor;
94 35 gtress
        ColonetServerInterface csi;
95 32 gtress
96
97
        public void init () {
98 136 gtress
                // set the default look and feel - choose one
99
        //String laf = UIManager.getSystemLookAndFeelClassName();
100 155 gtress
                String laf = UIManager.getCrossPlatformLookAndFeelClassName();
101
                //String laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
102 32 gtress
        try {
103
            UIManager.setLookAndFeel(laf);
104
        } catch (UnsupportedLookAndFeelException exc) {
105
            System.err.println ("Warning: UnsupportedLookAndFeel: " + laf);
106
        } catch (Exception exc) {
107
            System.err.println ("Error loading " + laf + ": " + exc);
108
        }
109
                // We should invoke and wait to avoid browser display difficulties
110
                Runnable r = new Runnable() {
111
                        public void run() {
112
                                createAndShowGUI();
113
                        }
114
                };
115
                try {
116
                        SwingUtilities.invokeAndWait(r);
117
                } catch (InterruptedException e) {
118
                        //Not really sure why we would be in this situation
119
                        System.out.println(e);
120
                } catch (java.lang.reflect.InvocationTargetException e) {
121 136 gtress
                        //This could happen for various reasons if there is a problem in createAndShowGUI
122
                        e.printStackTrace();
123 32 gtress
                }
124
        }
125
126
        public void destroy () {
127
                try { drawThread.interrupt(); } catch (Exception e) { }
128
                try { indicator.interrupt(); } catch (Exception e) { }
129
                try { packetMonitor.interrupt(); } catch (Exception e) { }
130
        }
131
132
        private synchronized void createAndShowGUI () {
133
                // init graphical elements
134 136 gtress
                // Get the graphics configuration of the screen to create a buffer
135
                gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
136
                        .getDefaultScreenDevice().getDefaultConfiguration();
137
                image = gc.createCompatibleImage(CANVAS_SIZE,CANVAS_SIZE);
138
                canvas = image.createGraphics();
139
                canvas.setStroke(new BasicStroke(2));  //set pen width
140
                panel = new ImagePanel(false, image);  //set automatic double-buffering to false. we are doing it manually.
141 32 gtress
142 136 gtress
                // Calculate center of canvas
143
                cx = image.getWidth() / 2;
144
                cy = image.getHeight() / 2;
145
146
                botFont = new Font("Arial", Font.PLAIN, 14);
147
                tokenLoc = 0;
148
                numBots = 0;
149
                selectedBot = 0;
150
151 32 gtress
                // Connection area
152 136 gtress
                txtMatrix = new JTextArea();
153 76 gtress
                txtMatrix.setBorder(BorderFactory.createTitledBorder("Input Matrix"));
154 32 gtress
                txtInfo = new JTextArea();
155
                txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
156
                txtInfo.setEditable(false);
157
                btnGraph = new JButton("Run");
158 155 gtress
                txtHost = new JTextField("roboclub9.frc.ri.cmu.edu");
159 32 gtress
                txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
160
                txtPort = new JTextField("10123");
161
                txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
162
                btnConnect = new JButton("Connect");
163
                lblConnectionStatus = new JLabel("Status: Offline");
164
                panelConnect = new JPanel();
165
                panelConnect.setLayout(new GridLayout(6,1));
166
                panelConnect.add(lblConnectionStatus);
167
                panelConnect.add(txtHost);
168
                panelConnect.add(txtPort);
169
                panelConnect.add(btnConnect);
170
                panelConnect.add(txtInfo);
171
                panelConnect.add(btnGraph);
172
                panelServerInterface = new JPanel();
173
                panelServerInterface.setLayout(new GridLayout(2,1));
174
                panelServerInterface.add(panelConnect);
175 76 gtress
                panelServerInterface.add(txtMatrix);
176 32 gtress
177
                // Status Elements
178
                lblTokenPasses = new JLabel();
179
                lblBattery = new JLabel("???");
180
                panelStats = new JPanel();
181
                panelStats.setLayout(new GridLayout(4,2));
182
                panelStats.add(new JLabel("Token Passes / sec          "));
183
                panelStats.add(lblTokenPasses);
184
                panelStats.add(new JLabel("Battery     "));
185
                panelStats.add(lblBattery);
186
                panelStats.add(new JLabel("Token Passes / sec     "));
187
                panelStats.add(lblTokenPasses);
188
189 72 gtress
                //TODO: add panelStats somewhere?
190 32 gtress
191
                // Robot direction panel
192
                panelRobotDirection = new JPanel();
193 136 gtress
                panelRobotDirectionButtons = new JPanel();
194 32 gtress
                btnF = new JButton("^");
195
                btnB = new JButton("v");
196
                btnL = new JButton("<");
197
                btnR = new JButton(">");
198
                btnActivate = new JButton("o");
199 136 gtress
                panelRobotDirectionButtons.setLayout(new GridLayout(1,5));
200
                panelRobotDirectionButtons.add(btnActivate);
201
                panelRobotDirectionButtons.add(btnF);
202
                panelRobotDirectionButtons.add(btnB);
203
                panelRobotDirectionButtons.add(btnL);
204
                panelRobotDirectionButtons.add(btnR);
205 32 gtress
206 136 gtress
                imageVectorControl = gc.createCompatibleImage(380, 210);
207
                vectorController = new VectorController(imageVectorControl);
208
                panelRobotDirection.setLayout(new BorderLayout());
209
                panelRobotDirection.add(vectorController, BorderLayout.CENTER);
210
                panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
211
212 32 gtress
                // Robot Control and Commands
213
                panelRobotCommands = new JPanel();
214
                panelRobotCommands.setLayout(new FlowLayout());
215 67 gtress
                cmbRobotNum = new JComboBox();
216
                panelRobotCommands.add(cmbRobotNum);
217 32 gtress
                panelRobotCommands.add(new JLabel("Commands go here"));
218
                panelRobotControl = new JPanel();
219
                panelRobotControl.setLayout(new GridLayout(2,1));
220
                panelRobotControl.add(panelRobotDirection);
221
                panelRobotControl.add(panelRobotCommands);
222
223
                // Task Manager
224
                panelTaskManager = new JPanel();
225
                panelTaskManager.setLayout(new BorderLayout());
226
                taskListModel = new DefaultListModel();
227
                taskList = new JList(taskListModel);
228
                taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
229
                taskList.setSelectedIndex(0);
230
                spTaskManager = new JScrollPane(taskList);
231
                panelTaskManagerControls = new JPanel();
232 136 gtress
                panelTaskManagerControls.setLayout(new GridLayout(1,4));
233 32 gtress
                panelTaskManagerControlsPriority = new JPanel();
234
                panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
235
                btnAddTask = new JButton("Add...");
236
                btnRemoveTask = new JButton("Remove");
237
                btnMoveTaskUp = new JButton("^");
238
                btnMoveTaskDown = new JButton("v");
239 136 gtress
                btnUpdateTasks = new JButton("Update");
240 32 gtress
                panelTaskManagerControlsPriority.add(btnMoveTaskUp);
241
                panelTaskManagerControlsPriority.add(btnMoveTaskDown);
242
                panelTaskManagerControls.add(btnAddTask);
243
                panelTaskManagerControls.add(btnRemoveTask);
244 136 gtress
                panelTaskManagerControls.add(btnUpdateTasks);
245 32 gtress
                panelTaskManagerControls.add(panelTaskManagerControlsPriority);
246
                panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
247
                panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
248
                panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
249
250 136 gtress
                // Task Manager Add Window
251
                taskAddWindow = new TaskAddWindow();
252
253 32 gtress
                // Message log
254
                log = new JTextArea();
255
                spLog = new JScrollPane(log,
256
                        ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
257
                        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
258
                spLog.setBorder(BorderFactory.createTitledBorder("Log"));
259
                spLog.setPreferredSize(new Dimension(0, 150));
260
                log.setEditable(false);
261
262
                // Main control mechanism
263
                panelControl = new JPanel();
264
                panelControl.setLayout(new GridLayout(1,1));
265
                tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
266 136 gtress
                tabPaneControl.setPreferredSize(new Dimension(400, 0));
267 32 gtress
                tabPaneControl.addTab("Connection", panelServerInterface);
268
                tabPaneControl.addTab("Robots", panelRobotControl);
269
                tabPaneControl.addTab("Tasks", panelTaskManager);
270
                panelControl.add(tabPaneControl);
271
272
                // Set up elements in the south
273
                panelSouth = new JPanel();
274
                panelSouth.setLayout(new GridLayout(1,2));
275
                panelSouth.add(spLog);
276
277
                this.getContentPane().setLayout(new BorderLayout());
278
                this.getContentPane().add(panel, BorderLayout.CENTER);
279
                this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
280
                this.getContentPane().add(panelControl, BorderLayout.EAST);
281
                this.setVisible(true);
282
283 72 gtress
                /* Add all listeners here */
284
                // Task Management
285 32 gtress
                btnAddTask.addActionListener(this);
286
                btnRemoveTask.addActionListener(this);
287
                btnMoveTaskUp.addActionListener(this);
288
                btnMoveTaskDown.addActionListener(this);
289 136 gtress
                btnUpdateTasks.addActionListener(this);
290 72 gtress
                // Robot Control
291
                btnF.addActionListener(this);
292
                btnB.addActionListener(this);
293
                btnL.addActionListener(this);
294
                btnR.addActionListener(this);
295
                btnActivate.addActionListener(this);
296 136 gtress
                vectorController.addMouseMotionListener(this);
297
                vectorController.addMouseListener(this);
298 72 gtress
                // Other
299 32 gtress
                btnGraph.addActionListener(this);
300
                btnConnect.addActionListener(this);
301
                panel.addMouseListener(this);
302 136 gtress
                this.addMouseMotionListener(this);
303 32 gtress
304 136 gtress
305 32 gtress
                // Set up dependent threads
306
                indicator = new SelectionIndicator(canvas);
307
                indicator.setRadius(RADIUS+3, 15);  //a tad more than the bot radius
308
                packetMonitor = new PacketMonitor();
309 76 gtress
                simulator = new Simulator();
310 35 gtress
311 136 gtress
                csi = new ColonetServerInterface(this);
312 32 gtress
313
        }
314
315 35 gtress
        public synchronized void paint (Graphics g) {
316 136 gtress
                /*        Redraw the graphical components in the applet.
317 32 gtress
                        This paint method overrides the built-in paint of the
318
                        JApplet, and we don't want to deal with redrawing the
319
                        components manually. Fuck that shit. */
320 136 gtress
                step();
321 32 gtress
                super.paint(g);
322
        }
323
324 35 gtress
        public synchronized void update (Graphics g) {
325 32 gtress
                paint(g);
326
        }
327
328 35 gtress
        public void actionPerformed (ActionEvent e) {
329 32 gtress
                Object source = e.getSource();
330
                if (source == btnGraph) {
331
                        btnGraph.setEnabled(false);
332
                        //Start dependent threads
333
                        drawThread = new Thread(this, "drawThread");
334
                        drawThread.start();
335
                        indicator.start();
336
                        packetMonitor.start();
337 76 gtress
                        simulator.start();
338 32 gtress
                } else if (source == btnConnect) {
339 155 gtress
                        csi.connect(txtHost.getText(), txtPort.getText());
340 76 gtress
                        dataUpdater = new DataUpdater();
341
                        dataUpdater.start();
342 136 gtress
                }
343
                // Robot Movement Controls
344
                else if (source == btnF) {
345
                        vectorController.setMaxForward();
346
                        vectorController.sendToServer();
347 72 gtress
                } else if (source == btnB) {
348 136 gtress
                        vectorController.setMaxReverse();
349
                        vectorController.sendToServer();
350 72 gtress
                } else if (source == btnL) {
351 136 gtress
                        vectorController.setMaxLeft();
352
                        vectorController.sendToServer();
353 72 gtress
                } else if (source == btnR) {
354 136 gtress
                        vectorController.setMaxRight();
355
                        vectorController.sendToServer();
356 72 gtress
                } else if (source == btnActivate) {
357 136 gtress
                        vectorController.setZero();
358
                        vectorController.sendToServer();
359
                }
360 107 gtress
361
                // Queue Management
362 136 gtress
                else if (source == btnAddTask) {
363
                        taskAddWindow.prompt();
364 107 gtress
                } else if (source == btnRemoveTask) {
365 136 gtress
                        if (taskList.getSelectedIndex() >= 0);
366
                                csi.sendQueueRemove(taskList.getSelectedIndex());
367
                        csi.sendQueueUpdate();
368 107 gtress
                } else if (source == btnMoveTaskUp) {
369 136 gtress
                        csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
370
                        csi.sendQueueUpdate();
371 107 gtress
                } else if (source == btnMoveTaskDown) {
372 136 gtress
                        csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
373
                        csi.sendQueueUpdate();
374
                } else if (source == btnUpdateTasks) {
375
                        csi.sendQueueUpdate();
376 32 gtress
                }
377
        }
378
379 35 gtress
        private void randomize () {
380 32 gtress
                Random r = new Random();
381
                StringBuilder s = new StringBuilder();
382
383
                int count = r.nextInt(8) + 1;
384 35 gtress
                for (int i = 0; i < count; i++) {
385
                        for (int j = 0; j < count; j++) {
386 32 gtress
                                if (r.nextBoolean())
387
                                        s.append("" + (r.nextInt(16) + 1));
388
                                else
389
                                        s.append("-");
390
                                if (j != count-1)
391
                                        s.append(" ");
392
                        }
393
                        if (i != count-1) s.append("\n");
394
                }
395
396 76 gtress
                txtMatrix.setText(s.toString());
397 32 gtress
        }
398
399 35 gtress
        public void drawRobot (int id, int x, int y) {
400 32 gtress
                //save the bot in memory, so we can tell if we click on it later
401
                botRect[id] = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS);
402
403
                //draw the bot on the canvas
404
                canvas.setColor(Color.BLACK);
405
                canvas.drawOval(x-RADIUS, y-RADIUS, RADIUS*2, RADIUS*2);
406
407
                //draw the label
408
                canvas.setFont(botFont);
409 136 gtress
                try {
410
                        canvas.drawString("" + xbeeID[id], x-20, y+2);
411
                } catch (Exception e) {
412
                        canvas.drawString("???", x-22, y+2);
413
                }
414 32 gtress
        }
415
416 35 gtress
        public void drawConnection (int start, int end, int radius, Color color) {
417 32 gtress
                final int ARROW_LENGTH = 18;
418
419
                double angle = 2.0 * Math.PI / numBots;
420
                int startx, starty, endx, endy;
421
                startx = cx - (int)(radius * Math.cos(start * angle));
422
                starty = cy - (int)(radius * Math.sin(start * angle));
423
                endx = cx - (int)(radius * Math.cos(end * angle));
424
                endy = cy - (int)(radius * Math.sin(end * angle));
425
                canvas.setColor(color);
426
                canvas.drawLine(startx, starty, endx, endy);
427
428
                //create arrow
429
                if (color.equals(Color.BLACK)) return;
430
                int big_dy = starty - endy;
431
                int big_dx = endx - startx;
432
                double theta = 0;
433
                if (big_dx == 0 && starty > endy) //pointing up
434
                        theta = Math.PI/2;
435
                else if (big_dx == 0 && starty < endy) //pointing down
436
                        theta = 3*Math.PI/2;
437
                else if (big_dy == 0 && startx > endx) //pointing left
438
                        theta = Math.PI;
439
                else if (big_dy == 0 && startx < endx) //pointing right
440
                        theta = 0;
441
                else
442
                        theta = Math.atan(1.0 * big_dy / big_dx);
443
444
                //create ploygon
445
                Polygon poly = new Polygon();
446
                int dx_arrow = Math.abs((int)(ARROW_LENGTH * Math.cos(theta)));
447
                int dy_arrow = Math.abs((int)(ARROW_LENGTH * Math.sin(theta)));
448
                int dy_half = (int)(ARROW_LENGTH/2 * Math.cos(theta));
449
                int dx_half = (int)(ARROW_LENGTH/2 * Math.sin(theta));
450
                int rx = (big_dx > 0) ? endx - dx_arrow : endx + dx_arrow;
451
                int ry = (big_dy > 0) ? endy + dy_arrow : endy - dy_arrow;
452
                poly.addPoint(endx, endy);
453
                poly.addPoint(rx - dx_half, ry - dy_half);
454
                poly.addPoint(rx + dx_half, ry + dy_half);
455
                canvas.fillPolygon(poly);
456
        }
457
458 35 gtress
        public void run () {
459
                while (true) {
460 32 gtress
                        repaint();
461 35 gtress
                        try {
462
                                Thread.sleep(90);
463
                        } catch (InterruptedException e) {
464
                                return;
465
                        }
466 32 gtress
                }
467
        }
468
469 35 gtress
        public void step () {
470 32 gtress
                final int DIAMETER = image.getWidth() - 2*BUFFER;
471
                final int BIGRADIUS = DIAMETER / 2;
472
                final int TOKENRADIUS = 40;
473
                boolean valid;
474
475
                // clear image
476
                canvas.setColor(Color.WHITE);
477
                canvas.fillRect(0, 0, image.getWidth(), image.getHeight());
478
479
                // parse the matrix, to see what robots exist
480 76 gtress
                String [] rows = txtMatrix.getText().split("\n");
481 32 gtress
                numBots = rows.length;
482
                String [][] entries = new String[numBots][numBots];
483
                valid = true;
484 35 gtress
                for (int i = 0; i < numBots; i++) {
485 32 gtress
                        entries[i] = rows[i].split(" ");
486
                        if (entries[i].length != rows.length) valid = false;
487
                }
488
489 35 gtress
                if (valid) {
490 38 gtress
                        this.showStatus("Running");
491 32 gtress
492
                        // draw robots and find which one is seleced
493
                        double angle = 2.0 * Math.PI / numBots;
494
                        canvas.setColor(Color.BLACK);
495
                        botRect = new Rectangle[numBots];
496
                        int x, y;
497
                        if (selectedBot >= numBots) selectedBot = 0;
498 35 gtress
                        for (int i = 0; i < numBots; i++) {
499 32 gtress
                                x = cx - (int)(BIGRADIUS * Math.cos(i * angle));
500
                                y = cy - (int)(BIGRADIUS * Math.sin(i * angle));
501
                                drawRobot(i, x, y);
502
                                if (i == selectedBot) indicator.setCenter(x, y);
503
                        }
504
505
                        // draw token marker
506
                        int tokenx, tokeny;
507
                        int tokenNum = tokenLoc;
508
                        tokenx = cx - (int)(BIGRADIUS * Math.cos(tokenNum * angle));
509
                        tokeny = cy - (int)(BIGRADIUS * Math.sin(tokenNum * angle));
510
                        canvas.setColor(Color.RED);
511
                        canvas.drawOval(tokenx-TOKENRADIUS, tokeny-TOKENRADIUS, 2*TOKENRADIUS, 2*TOKENRADIUS);
512
513
                        // create an inner circle along which the connections are made.
514
                        // let the diameter of this circle be 2*RADIUS less than the outerDiameter.
515
                        // see what connections exist
516 35 gtress
                        for (int row = 0; row < numBots; row++) {
517
                                for(int col = 0; col < numBots; col++) {
518
                                        if (!entries[row][col].equals("-") && entries[col][row].equals("-") && row != col) {
519
                                                //TODO: Make a standard gray
520 32 gtress
                                                drawConnection(row, col, BIGRADIUS-RADIUS, new Color(200,200,200));
521 38 gtress
                                        } else if (!entries[row][col].equals("-") && ! entries[col][row].equals("-") && row != col) {
522 32 gtress
                                                drawConnection(row, col, BIGRADIUS-RADIUS, Color.BLACK);
523
                                        }
524
                                }
525
                        }
526
527
                        // draw the selection indicator
528
                        indicator.draw();
529
530 35 gtress
                } else {// if matrix is not valid
531 32 gtress
                        this.showStatus("Error: Invalid matrix");
532
                }
533
534
        }
535
536
        /*        At this point, moveToken is only called by the simulator.
537
        *        In the future, it can be rewritten to account for non-standard
538
        *        token passing or deleted if the information can be retrieved
539
        *        directly from the Colonet server instead.
540
        */
541 35 gtress
        public void moveToken () {
542
                try {
543
                        tokenLoc = (tokenLoc+1)%numBots;
544
                } catch (ArithmeticException e) {  // in case numRobots is zero
545
                }
546 32 gtress
547
                packetMonitor.addTokenPass();
548
        }
549
550 136 gtress
        public JTextArea getLog () {
551
                return log;
552
        }
553
554
        public JTextArea getMatrixInput () {
555
                return txtMatrix;
556
        }
557
558
        public void parseMatrix (String line) {
559
                txtMatrix.setText("");
560
                String [] str = line.split(" ");
561
                int num = Integer.parseInt(str[2]);
562
                for (int i = 0; i < num; i++) {
563
                        for (int j = 0; j < num; j++) {
564
                                String next = str[3 + i*num + j];
565
                                if (next.equals("-1"))
566
                                        txtMatrix.append("-");
567
                                else
568
                                        txtMatrix.append(next);
569
                                if (j < num - 1)
570
                                        txtMatrix.append(" ");
571
                        }
572
                        if (i < num - 1)
573
                                txtMatrix.append("\n");
574
                }
575
576
        }
577
578
        public void parseQueue (String line) {
579
                log.append("Got queue update\n");
580
581
        }
582
583
        public void parseXBeeIDs (String line) {
584
                String [] str = line.split(" ");
585
                int num = Integer.parseInt(str[2]);
586
                xbeeID = new int[num];
587
                for (int i = 0; i < num; i++)
588
                        xbeeID[i] = Integer.parseInt(str[i+3]);
589
590
                //update the list of robots to control
591
                cmbRobotNum.removeAllItems();
592
                cmbRobotNum.addItem(new String("   All   "));
593
                for (int i = 0; i < num; i++)
594
                        cmbRobotNum.addItem(new String("" + xbeeID[i]));
595
        }
596
597 32 gtress
        //
598
        // MouseEvent methods
599
        //
600 155 gtress
        public void mouseExited(MouseEvent e) {
601
        }
602
        public void mouseEntered(MouseEvent e) {
603
        }
604 136 gtress
        public void mouseReleased(MouseEvent e) {
605
                vectorController.sendToServer();
606
        }
607
        public void mouseClicked(MouseEvent e) {
608
                mouseDragged(e);
609
        }
610 35 gtress
        public void mousePressed(MouseEvent e) {
611 32 gtress
                try {
612 35 gtress
                        for (int i = 0; i < numBots; i++) {
613 32 gtress
                                if (botRect[i].contains(e.getPoint()))
614
                                        selectedBot = i;
615
                        }
616
                } catch (Exception ex) {
617
                        System.out.println(e);
618
                }
619
620
        }
621 136 gtress
        public void mouseDragged(MouseEvent e) {
622
                vectorController.setPoint(e.getX(), e.getY());
623
                vectorController.repaint();
624
        }
625 155 gtress
        public void mouseMoved(MouseEvent e) {
626
        }
627 32 gtress
628 136 gtress
629 32 gtress
        /*
630
        *        SelectionIndicator thread.
631
        *        Graphical representation of the selection marker
632
        *
633
        *        step() and draw() are synchronized methods. step() is private and
634
        *        used to update the position of the crosshairs. draw() is called
635
        *        externally and should only run if all calculations in step() have
636
        *        been completed.
637
        */
638 35 gtress
        private class SelectionIndicator extends Thread {
639 32 gtress
640
                final int INDICATOR_DELAY = 100;
641 155 gtress
                final double DTHETA = 0.4;    //larger values make the marker rotate faster
642 32 gtress
                Graphics2D g;   //canvas to draw on
643
                boolean running;
644
645
                int sx, sy;                //center
646
                int r, dr;                //radius and width of marker
647
                double theta;   //current angle
648
649
                volatile Polygon poly1, poly2, poly3, poly4;
650
651
                int px1, py1;
652
                int rx1, ry1;
653
                int px2, py2;
654
                int rx2, ry2;
655
                int px3, py3;
656
                int rx3, ry3;
657
                int px4, py4;
658
                int rx4, ry4;
659
660
                int steps;
661
662 35 gtress
                public SelectionIndicator (Graphics2D g) {
663 32 gtress
                        super("SelectionIndicator");
664
                        this.g = g;
665
                        running = false;
666
                        steps = 0;
667
668
                        theta = 0;
669
                        rx1 = 0; ry1 = 0;
670
                        px1 = 0; py1 = 0;
671
                        rx2 = 0; ry2 = 0;
672
                        px2 = 0; py2 = 0;
673
                        rx3 = 0; ry3 = 0;
674
                        px3 = 0; py3 = 0;
675
                        rx4 = 0; ry4 = 0;
676
                        px4 = 0; py4 = 0;
677
                }
678
679 35 gtress
                public synchronized void setCenter (int sx, int sy) {
680 32 gtress
                        if (sx == this.sx && sy == this.sy) return;
681
                        this.sx = sx;
682
                        this.sy = sy;
683
                        steps = 0;
684
                }
685
686 35 gtress
                public synchronized void setRadius (int r, int dr) {
687 32 gtress
                        this.r = r;
688
                        this.dr = dr;
689
                        steps = 0;
690
                }
691
692 35 gtress
                public void run () {
693 32 gtress
                        running = true;
694 35 gtress
                        while (running) {
695 32 gtress
                                step();
696 35 gtress
                                try {
697
                                        Thread.sleep(INDICATOR_DELAY);
698
                                } catch (InterruptedException e) {
699
                                        running = false;
700
                                        return;
701
                                }
702 32 gtress
                        }
703
                }
704
705 35 gtress
                private synchronized void step () {
706 32 gtress
                        Polygon poly1_new = new Polygon();
707
                        Polygon poly2_new = new Polygon();
708
                        Polygon poly3_new = new Polygon();
709
                        Polygon poly4_new = new Polygon();
710
711
                        //the step
712
                        theta = (theta + DTHETA/Math.PI) % (Math.PI);
713
714
                        //the calculation
715
                        //let p be the point of the pointy thing toward the center
716
                        //let r be the point at the opposite side
717
718
                        //recalculate radius, if it will look cool, lolz
719
                        int newr = r;
720 38 gtress
                        if (steps < 100)
721 107 gtress
                        newr = (int)( r + 200/(steps+1) );
722 32 gtress
723
                        //precompute values for dx and dy
724
                        int dx_inner = (int)(newr * Math.cos(theta));
725
                        int dy_inner = (int)(newr * Math.sin(theta));
726
                        int dx_outer = (int)((newr+dr) * Math.cos(theta));
727
                        int dy_outer = (int)((newr+dr) * Math.sin(theta));
728
729
                        //calculate polygon constants
730
                        int dy_poly = (int)(dr/2 * Math.cos(theta));
731
                        int dx_poly = (int)(dr/2 * Math.sin(theta));
732
733
                        //determine critical points
734 35 gtress
                        //kansas city shuffle!
735 32 gtress
                        px1 = sx + dx_inner;
736
                        py1 = sy - dy_inner;
737
                        rx1 = sx + dx_outer;
738
                        ry1 = sy - dy_outer;
739
                        px2 = sx - dx_inner;
740
                        py2 = sy + dy_inner;
741
                        rx2 = sx - dx_outer;
742
                        ry2 = sy + dy_outer;
743
                        px3 = sx - dy_inner;
744
                        py3 = sy - dx_inner;
745
                        rx3 = sx - dy_outer;
746
                        ry3 = sy - dx_outer;
747
                        px4 = sx + dy_inner;
748
                        py4 = sy + dx_inner;
749
                        rx4 = sx + dy_outer;
750
                        ry4 = sy + dx_outer;
751
752
                        //create polygons
753
                        poly1_new.addPoint(px1, py1);
754
                        poly1_new.addPoint(rx1+dx_poly, ry1+dy_poly);
755
                        poly1_new.addPoint(rx1-dx_poly, ry1-dy_poly);
756
                        poly2_new.addPoint(px2, py2);
757
                        poly2_new.addPoint(rx2+dx_poly, ry2+dy_poly);
758
                        poly2_new.addPoint(rx2-dx_poly, ry2-dy_poly);
759
                        poly3_new.addPoint(px3, py3);
760
                        poly3_new.addPoint(rx3-dy_poly, ry3+dx_poly);
761
                        poly3_new.addPoint(rx3+dy_poly, ry3-dx_poly);
762
                        poly4_new.addPoint(px4, py4);
763
                        poly4_new.addPoint(rx4-dy_poly, ry4+dx_poly);
764
                        poly4_new.addPoint(rx4+dy_poly, ry4-dx_poly);
765
766
                        //reassign updated polygons
767
                        poly1 = poly1_new;
768
                        poly2 = poly2_new;
769
                        poly3 = poly3_new;
770
                        poly4 = poly4_new;
771
772
                        if (steps < 300) steps++;
773
                }
774
775 35 gtress
                public synchronized void draw () {
776 32 gtress
                        if (!running) return;
777
                        g.setColor(Color.GRAY);
778
                        //draw polygons
779
                        g.fillPolygon(poly1);
780
                        g.fillPolygon(poly2);
781
                        g.fillPolygon(poly3);
782
                        g.fillPolygon(poly4);
783
                }
784
785
        }
786
787
        /*
788
        *        Simulator thread.
789
        *
790
        */
791 35 gtress
        private class Simulator extends Thread {
792 32 gtress
                final int SIMULATOR_DELAY = 300;
793
                boolean running;
794
795 35 gtress
                public Simulator () {
796 32 gtress
                        super("Simulator");
797
                        running = false;
798
                }
799
800 35 gtress
                public void run () {
801 32 gtress
                        running = true;
802 35 gtress
                        while (running) {
803 32 gtress
                                step();
804 35 gtress
                                try {
805
                                        Thread.sleep(SIMULATOR_DELAY);
806
                                } catch (InterruptedException e) {
807
                                        running = false;
808
                                        return;
809
                                }
810 32 gtress
                        }
811
                }
812
813 35 gtress
                private void step () {
814 155 gtress
                        // don't do anything! the colonet should work on its own!
815 32 gtress
                }
816
817
        }
818
819
        /*
820
        *        PacketMonitor thread.
821
        *
822
        *        Currently, this counts the rate of token passes but will eventually
823
        *        be modified to keep more important statistics.
824
        */
825 35 gtress
        private class PacketMonitor extends Thread {
826 32 gtress
                final int PACKETMONITOR_DELAY = 1000;
827
828
                boolean running;
829
                int tokenPasses;
830
831 35 gtress
                public PacketMonitor () {
832 32 gtress
                        super("PacketMonitor");
833
                        running = false;
834
                        tokenPasses = 0;
835
                }
836
837 35 gtress
                public void run () {
838 32 gtress
                        running = true;
839 35 gtress
                        while (running) {
840 32 gtress
                                displayTokenPasses();
841 35 gtress
                                try {
842
                                        Thread.sleep(PACKETMONITOR_DELAY);
843
                                } catch (InterruptedException e) {
844
                                        running = false;
845
                                        return;
846
                                }
847 32 gtress
                        }
848
                }
849
850 35 gtress
                public synchronized void addTokenPass () {
851 32 gtress
                        tokenPasses++;
852
                }
853
854 35 gtress
                public synchronized void displayTokenPasses () {
855 32 gtress
                        lblTokenPasses.setText("" + tokenPasses);
856
                        tokenPasses = 0;
857
                }
858
859
        }
860
861
        /*
862 67 gtress
        *        DataUpdater thread.
863 76 gtress
        *   The purpose of this thread is to request data from the server at regular intervals.
864 32 gtress
        *
865
        */
866 67 gtress
        class DataUpdater extends Thread {
867 155 gtress
                final int DATAUPDATER_DELAY = 5000;
868 32 gtress
869 67 gtress
                public DataUpdater () {
870
                        super("Colonet DataUpdater");
871 32 gtress
                }
872
873 35 gtress
                public void run () {
874 32 gtress
                        String line;
875 35 gtress
                        while (true) {
876 32 gtress
                                try {
877 76 gtress
                                        //request more data
878 136 gtress
                                        if (csi.isReady()) {
879 107 gtress
                                                csi.sendSensorDataRequest();
880 136 gtress
                                                csi.sendXBeeIDRequest();
881
                                        }
882 67 gtress
                                        Thread.sleep(DATAUPDATER_DELAY);
883 32 gtress
                                } catch (InterruptedException e) {
884
                                        return;
885 39 gtress
                                }
886 32 gtress
                        }
887
                }
888
889
        }
890 136 gtress
891
        /*
892
        *        ImagePanel class
893
        *        Enables more efficient image handling in a component-controlled environment
894
        */
895
        class ImagePanel extends JPanel {
896
                protected Image img;
897
898
                public ImagePanel (Image img) {
899
                        super();
900
                        this.img = img;
901
                }
902
903
                public ImagePanel (boolean isDoubleBuffered, Image img) {
904
                        super(isDoubleBuffered);
905
                        this.img = img;
906
                }
907
908
                public void paint (Graphics g) {
909
                        // Place the buffered image on the screen, inside the panel
910
                        g.drawImage(img, 0, 0, Color.WHITE, this);
911
                }
912
913
        }
914
915
        /*
916
        *        VectorController class
917
        *        Manages robot motion control graphically
918
        */
919
        class VectorController extends ImagePanel {
920
                int x, y, cx, cy;
921
                int width, height;
922
                int side;
923
924
                public VectorController (Image img) {
925
                        super (img);
926
                        width = img.getWidth(null);
927
                        height = img.getHeight(null);
928
                        cx = img.getWidth(null)/2;
929
                        cy = img.getHeight(null)/2;
930
                        x = cx;
931
                        y = cy;
932
                        if (width < height)
933
                                side = width;
934
                        else
935
                                side = height;
936
                }
937
938
                public void setPoint (int x, int y) {
939
                        if (!isValidPoint(x, y))
940
                                return;
941
                        this.x = x;
942
                        this.y = y;
943
                        repaint();
944
                }
945
946
                public boolean isValidPoint (int x, int y) {
947
                        double xterm = Math.pow(1.0*(x - cx)/(side/2), 2);
948
                        double yterm = Math.pow(1.0*(y - cy)/(side/2), 2);
949
                        return (xterm + yterm <= 1);
950
                }
951
952
                public int getVelocity () {
953
                        int dx = x - cx;
954
                        int dy = y - cy;
955
                        int v = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) );
956
                        return v;
957
                }
958
959 155 gtress
                public int getAngle () {
960 136 gtress
                        int dx = x - cx;
961
                        int dy = cy - y;
962
                        double theta = Math.atan2(Math.abs(dx), Math.abs(dy));
963
                        theta = (int) (1.0 * theta * 180 / Math.PI);  //transform to degrees
964
                        if (dy < 0)
965
                                theta = 180 - theta;
966
                        theta *= Math.signum(dx);
967 155 gtress
                        return (int) theta;
968 136 gtress
                }
969
970
                public void paint (Graphics g) {
971
                        g.setColor(Color.BLACK);
972
                        g.fillRect(0, 0, width, height);
973
                        ((Graphics2D)g).setStroke(new BasicStroke(1));
974
                        g.setColor(Color.RED);
975
                        g.drawOval(cx-side/2, cy-side/2, side, side);
976
                        ((Graphics2D)g).setStroke(new BasicStroke(2));
977
                        g.setColor(Color.GREEN);
978
                        g.drawLine(cx, cy, x, y);
979
                        g.fillOval(x-3, y-3, 6, 6);
980
                }
981
982
                public void setMaxForward () {
983
                        setPoint(cx, cy - (side/2) + 1);
984
                }
985
986
                public void setMaxReverse () {
987
                        setPoint(cx, cy + (side/2) - 1);
988
                }
989
990
                public void setMaxLeft () {
991
                        setPoint(cx - (side/2) + 1, cy);
992
                }
993
994
                public void setMaxRight () {
995
                        setPoint(cx + (side/2) - 1, cy);
996
                }
997
998
                public void setZero () {
999
                        setPoint(cx, cy);
1000
                }
1001
1002
                public void sendToServer () {
1003
                        if (csi != null)
1004
                                csi.sendData(ColonetServerInterface.MOVE + " " +
1005
                                        vectorController.getAngle() + " " +
1006
                                        vectorController.getVelocity(), ColonetServerInterface.GLOBAL_DEST);
1007
                }
1008
1009
        }
1010
1011
        /*
1012
        *        TaskAddWindow class
1013
        *        makes it easy to add tasks to the queue
1014
        */
1015
        class TaskAddWindow extends JFrame implements ActionListener, ListSelectionListener {
1016
                JPanel panelButtons;
1017
                JPanel panelParameters;
1018
                JPanel panelSouth;
1019
                JPanel panelSelection;
1020
                JButton btnSubmit;
1021
                JButton btnCancel;
1022
                DefaultListModel availableListModel;
1023
                JList availableList;
1024
                JScrollPane spAvailableTasks;
1025
                JTextArea txtDescription;
1026
                JTextField txtParameters;
1027
                MouseListener mouseListener;
1028
1029
                public TaskAddWindow () {
1030
                        super("Add a Task ... if you dare!");
1031
                        super.setSize(500,500);
1032
                        super.setLayout(new BorderLayout());
1033
1034
                        // set up buttons
1035
                        btnSubmit = new JButton("Submit");
1036
                        btnCancel = new JButton("Cancel");
1037
                        panelButtons = new JPanel();
1038
                        panelButtons.setLayout(new FlowLayout());
1039
                        panelButtons.add(btnSubmit);
1040
                        panelButtons.add(btnCancel);
1041
                        this.getRootPane().setDefaultButton(btnSubmit);
1042
1043
                        // set up task list
1044
                        availableListModel = new DefaultListModel();
1045
                        availableListModel.addElement("Map the Environment");
1046
                        availableListModel.addElement("Clean Up Chemical Spill");
1047
                        availableListModel.addElement("Grow Plants");
1048
                        availableListModel.addElement("Save the Cheerleader");
1049
                        availableListModel.addElement("Save the World");
1050
                        availableList = new JList(availableListModel);
1051
                        availableList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1052
                        availableList.setSelectedIndex(-1);
1053
                        spAvailableTasks = new JScrollPane(availableList);
1054
                        spAvailableTasks.setBorder(BorderFactory.createTitledBorder("Select A Task"));
1055
                        txtDescription = new JTextArea();
1056
                        txtDescription.setEditable(false);
1057
                        txtDescription.setLineWrap(true);
1058
                        txtDescription.setWrapStyleWord(true);
1059
                        txtDescription.setBorder(BorderFactory.createTitledBorder("Description"));
1060
1061
                        //set up parameter area
1062
                        panelParameters = new JPanel();
1063
                        panelParameters.setLayout(new BorderLayout());
1064
                        txtParameters = new JTextField();
1065
                        panelParameters.add(new JLabel("Optional parameters for this task: "), BorderLayout.WEST);
1066
                        panelParameters.add(txtParameters);
1067
1068
                        // assemble objects
1069
                        panelSelection = new JPanel();
1070
                        panelSelection.setLayout(new GridLayout(1,2));
1071
                        panelSelection.add(spAvailableTasks);
1072
                        panelSelection.add(txtDescription);
1073
1074
                        panelSouth = new JPanel();
1075
                        panelSouth.setLayout(new GridLayout(2,1));
1076
                        panelSouth.add(panelParameters);
1077
                        panelSouth.add(panelButtons);
1078
1079
                        this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
1080
                        this.getContentPane().add(panelSelection, BorderLayout.CENTER);
1081
                        this.setLocationRelativeTo(null);
1082
1083
                        // add listeners here
1084
                        availableList.addListSelectionListener(this);
1085
                        btnSubmit.addActionListener(this);
1086
                        btnCancel.addActionListener(this);
1087
                }
1088
1089
                public void prompt () {
1090
                        this.setVisible(true);
1091
                }
1092
1093
                private String getDescription (int index) {
1094
                        if (index < 0)
1095
                                return "";
1096
                        switch (index) {
1097
                                case 0: return "SLAM and junk";
1098
                                case 1: return "I'm not sure this works";
1099
                                case 2: return "Push them into the light";
1100
                                case 3: return "Watch out for clock repair guys";
1101
                                case 4: return "But make sure he's dead, geez, why would you let him get away? I mean come on, "
1102
                                        +"he's just lying there and everyone's too busy looking at people flying through the sky to "
1103
                                        +"notice? Oh yeah, that's a good transition to a new season, let's make the audience think "
1104
                                        +"he's dead and then pull a fast one on them, they'll never see that coming.";
1105
1106
                                default: return "Task not recognized";
1107
                        }
1108
                }
1109
1110
                public void actionPerformed (ActionEvent e) {
1111
                        Object source = e.getSource();
1112
                        if (source == btnSubmit) {
1113
                                txtParameters.setText(txtParameters.getText().trim());
1114
1115
1116
                                this.setVisible(false);
1117
                        } else if (source == btnCancel) {
1118
                                this.setVisible(false);
1119
                        }
1120
                }
1121
1122
                public void valueChanged (ListSelectionEvent e) {
1123
                        int index = availableList.getSelectedIndex();
1124
                        if (index >= 0)
1125
                                txtDescription.setText(getDescription(index));
1126
                }
1127
1128
        }
1129 32 gtress
1130
}