Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (46.4 KB)

1 32 gtress
//
2
//  Colonet.java
3
//
4
5
import javax.swing.*;
6 320 gtress
import javax.swing.event.*;
7
import javax.imageio.*;
8 32 gtress
import java.awt.*;
9
import java.awt.image.*;
10
import java.awt.event.*;
11
import java.net.*;
12
import java.io.*;
13 427 gtress
import java.util.*;
14 32 gtress
15
16 320 gtress
/**
17
*        The Colonet Graphical User Interface Applet for use locally and over an internet connection.
18
*        @author Gregory Tress
19
*
20 341 gtress
*        To generate javadoc on this file or other java files, use javadoc *.java -d doc, where doc
21
*        is the name of the folder into which the files should be written.
22 320 gtress
*/
23
public class Colonet extends JApplet implements ActionListener, MouseInputListener, KeyListener, Runnable {
24
25 427 gtress
        // Used for images
26 320 gtress
        final int CANVAS_SIZE = 500;  //the applet may be slow if the canvas gets too large
27 32 gtress
        final int BUFFER = 50;
28
        final int RADIUS = 30;
29 341 gtress
30
        //Used for the robot controller
31
        final int VECTOR_CONTROLLER_HEIGHT = 220;
32 420 gtress
        final int VECTOR_CONTROLLER_WIDTH = 350;
33 341 gtress
34 32 gtress
35
        // Connection
36
        JTextField txtHost;
37
        JTextField txtPort;
38
        JButton btnConnect;
39 414 gtress
        JButton btnGetXBeeIDs;
40 32 gtress
        JLabel lblConnectionStatus;
41 320 gtress
        JTextArea txtMatrix;
42 32 gtress
        JTextArea txtInfo;
43
        JPanel panelConnect;
44
        JPanel panelServerInterface;
45 427 gtress
        Socket socket;
46
        OutputStreamWriter out;
47
        DataUpdater dataUpdater;
48 32 gtress
49
        // South
50
        JPanel panelSouth;
51
        JTextArea log;
52
        JScrollPane spLog;
53
54
        // Control
55
        JPanel panelControl;
56
        JTabbedPane tabPaneControl;
57
        JPanel panelRobotControl;
58
        JPanel panelRobotDirection;
59 320 gtress
        JPanel panelRobotDirectionButtons;
60 32 gtress
        JPanel panelRobotCommands;
61
        JButton btnF, btnB, btnL, btnR, btnActivate;
62 320 gtress
        JComboBox cmbRobotNum;
63
        JLabel lblBattery;
64 428 gtress
        JLabel lblSelected;
65 333 gtress
        BatteryIcon batteryIcon;
66 320 gtress
        JPanel panelBattery;
67
        VectorController vectorController;
68
        BufferedImage imageVectorControl;
69 427 gtress
        JButton btnAssignID;
70 428 gtress
        boolean setWaypoint;
71 429 gtress
        int setWaypointID;
72 428 gtress
        JButton btnCommand_MoveTo;
73 429 gtress
        JButton btnCommand_MoveAll;
74 320 gtress
        JButton btnCommand_StopTask;
75
        JButton btnCommand_ResumeTask;
76
        JButton btnCommand_ChargeNow;
77
        JButton btnCommand_StopCharging;
78 32 gtress
79
        // Task Manager
80
        JPanel panelTaskManager;
81
        JScrollPane spTaskManager;
82
        JPanel panelTaskManagerControls;
83
        JPanel panelTaskManagerControlsPriority;
84
        DefaultListModel taskListModel;
85
        JList taskList;
86
        JButton btnAddTask;
87
        JButton btnRemoveTask;
88
        JButton btnMoveTaskUp;
89
        JButton btnMoveTaskDown;
90 320 gtress
        JButton btnUpdateTasks;
91
        TaskAddWindow taskAddWindow;
92 32 gtress
93 427 gtress
        //Webcam
94 320 gtress
        WebcamPanel panelWebcam;
95
        GraphicsPanel panelGraph;
96 32 gtress
        GraphicsConfiguration gc;
97
        volatile BufferedImage image;
98
        volatile Graphics2D canvas;
99
        int cx, cy;
100 320 gtress
        JTabbedPane tabPaneMain;
101 32 gtress
102
        Font botFont;
103
        volatile int numBots;
104 427 gtress
        volatile int selectedBot;  //the user has selected this bot graphically
105
        volatile java.util.List <RobotIcon> robotIcons;  //contains boundary shapes around bots for click detection
106 320 gtress
        volatile int[] xbeeID;
107 32 gtress
108 428 gtress
        Thread paintThread;
109 32 gtress
        SelectionIndicator indicator;
110 320 gtress
        WebcamLoader webcamLoader;
111 35 gtress
        ColonetServerInterface csi;
112 32 gtress
113
114
        public void init () {
115 427 gtress
                // Set the default look and feel - choose one
116 320 gtress
        //String laf = UIManager.getSystemLookAndFeelClassName();
117
                String laf = UIManager.getCrossPlatformLookAndFeelClassName();
118
                //String laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
119 32 gtress
        try {
120
            UIManager.setLookAndFeel(laf);
121
        } catch (UnsupportedLookAndFeelException exc) {
122
            System.err.println ("Warning: UnsupportedLookAndFeel: " + laf);
123
        } catch (Exception exc) {
124
            System.err.println ("Error loading " + laf + ": " + exc);
125
        }
126
                // We should invoke and wait to avoid browser display difficulties
127
                Runnable r = new Runnable() {
128
                        public void run() {
129
                                createAndShowGUI();
130
                        }
131
                };
132
                try {
133
                        SwingUtilities.invokeAndWait(r);
134
                } catch (InterruptedException e) {
135
                        //Not really sure why we would be in this situation
136 320 gtress
                        System.out.println("InterruptedException in init: " + e);
137 32 gtress
                } catch (java.lang.reflect.InvocationTargetException e) {
138 320 gtress
                        //This could happen for various reasons if there is a problem in createAndShowGUI
139
                        e.printStackTrace();
140 32 gtress
                }
141
        }
142
143
        public void destroy () {
144 428 gtress
                try { paintThread.interrupt(); } catch (Exception e) { }
145 32 gtress
                try { indicator.interrupt(); } catch (Exception e) { }
146
        }
147
148
        private synchronized void createAndShowGUI () {
149
                // init graphical elements
150 320 gtress
                // Get the graphics configuration of the screen to create a buffer
151
                gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
152
                        .getDefaultScreenDevice().getDefaultConfiguration();
153
                image = gc.createCompatibleImage(CANVAS_SIZE,CANVAS_SIZE);
154
                canvas = image.createGraphics();
155
                canvas.setStroke(new BasicStroke(2));  //set pen width
156 341 gtress
                panelGraph = new GraphicsPanel(image, false);  //set automatic double-buffering to false. we are doing it manually.
157 320 gtress
                panelWebcam = new WebcamPanel();
158
                tabPaneMain = new JTabbedPane();
159
                tabPaneMain.add(panelWebcam, "Webcam");
160 429 gtress
                //tabPaneMain.add(panelGraph, "Graph");
161 32 gtress
162 320 gtress
                // Calculate center of canvas
163
                cx = image.getWidth() / 2;
164
                cy = image.getHeight() / 2;
165
166 427 gtress
                // Set up robots
167 320 gtress
                botFont = new Font("Arial", Font.PLAIN, 14);
168
                numBots = 0;
169 428 gtress
                selectedBot = -1;
170 427 gtress
                robotIcons = new ArrayList <RobotIcon> ();
171 320 gtress
172 32 gtress
                // Connection area
173 320 gtress
                txtMatrix = new JTextArea();
174
                txtMatrix.setBorder(BorderFactory.createTitledBorder("Input Matrix"));
175 32 gtress
                txtInfo = new JTextArea();
176
                txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
177
                txtInfo.setEditable(false);
178 385 gtress
                txtHost = new JTextField("localhost");
179 32 gtress
                txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
180
                txtPort = new JTextField("10123");
181
                txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
182
                btnConnect = new JButton("Connect");
183 414 gtress
                btnGetXBeeIDs = new JButton("Get XBee IDs");
184 389 gtress
                getRootPane().setDefaultButton(btnConnect);
185 32 gtress
                lblConnectionStatus = new JLabel("Status: Offline");
186
                panelConnect = new JPanel();
187
                panelConnect.setLayout(new GridLayout(6,1));
188
                panelConnect.add(lblConnectionStatus);
189
                panelConnect.add(txtHost);
190
                panelConnect.add(txtPort);
191
                panelConnect.add(btnConnect);
192 414 gtress
                panelConnect.add(btnGetXBeeIDs);
193 32 gtress
                panelServerInterface = new JPanel();
194
                panelServerInterface.setLayout(new GridLayout(2,1));
195
                panelServerInterface.add(panelConnect);
196 320 gtress
                panelServerInterface.add(txtMatrix);
197
198 32 gtress
                // Robot direction panel
199
                panelRobotDirection = new JPanel();
200 320 gtress
                panelRobotDirectionButtons = new JPanel();
201 32 gtress
                btnF = new JButton("^");
202
                btnB = new JButton("v");
203
                btnL = new JButton("<");
204
                btnR = new JButton(">");
205
                btnActivate = new JButton("o");
206 320 gtress
                panelRobotDirectionButtons.setLayout(new GridLayout(1,5));
207
                panelRobotDirectionButtons.add(btnActivate);
208
                panelRobotDirectionButtons.add(btnF);
209
                panelRobotDirectionButtons.add(btnB);
210
                panelRobotDirectionButtons.add(btnL);
211
                panelRobotDirectionButtons.add(btnR);
212 32 gtress
213 341 gtress
                imageVectorControl = gc.createCompatibleImage(VECTOR_CONTROLLER_WIDTH, VECTOR_CONTROLLER_HEIGHT);
214 320 gtress
                vectorController = new VectorController(imageVectorControl);
215
                panelRobotDirection.setLayout(new BorderLayout());
216
                panelRobotDirection.add(vectorController, BorderLayout.CENTER);
217
                panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
218
219 32 gtress
                // Robot Control and Commands
220
                panelRobotCommands = new JPanel();
221 320 gtress
                panelRobotCommands.setLayout(new GridLayout(5,2));
222
                cmbRobotNum = new JComboBox();
223 333 gtress
                // Battery subset
224 341 gtress
                batteryIcon = new BatteryIcon(25);
225 333 gtress
                lblBattery = new JLabel(batteryIcon);
226 428 gtress
                lblSelected = new JLabel("None");
227
                // Command subset
228
                setWaypoint = false;
229 429 gtress
                setWaypointID = -1;
230 427 gtress
                btnAssignID = new JButton("Assign ID");
231 428 gtress
                btnCommand_MoveTo = new JButton("Move to ...");
232 429 gtress
                btnCommand_MoveAll = new JButton("Move all ...");
233 320 gtress
                btnCommand_StopTask = new JButton("Stop Current Task");
234
                btnCommand_ResumeTask = new JButton("Resume Current Task");
235
                btnCommand_ChargeNow = new JButton("Recharge Now");
236
                btnCommand_StopCharging = new JButton("Stop Recharging");
237
                panelRobotCommands.add(new JLabel("Select Robot to Control: "));
238
                panelRobotCommands.add(cmbRobotNum);
239
                panelRobotCommands.add(new JLabel("Battery Level: "));
240
                panelRobotCommands.add(lblBattery);
241 428 gtress
                panelRobotCommands.add(new JLabel("Selected Icon: "));
242
                panelRobotCommands.add(lblSelected);
243 427 gtress
                panelRobotCommands.add(btnAssignID);
244 429 gtress
                panelRobotCommands.add(new JLabel(""));
245 428 gtress
                panelRobotCommands.add(btnCommand_MoveTo);
246 429 gtress
                panelRobotCommands.add(btnCommand_MoveAll);
247 427 gtress
                //panelRobotCommands.add(btnCommand_StopTask);
248
                //panelRobotCommands.add(btnCommand_ResumeTask);
249
                //panelRobotCommands.add(btnCommand_ChargeNow);
250
                //panelRobotCommands.add(btnCommand_StopCharging);
251 32 gtress
                panelRobotControl = new JPanel();
252
                panelRobotControl.setLayout(new GridLayout(2,1));
253
                panelRobotControl.add(panelRobotDirection);
254
                panelRobotControl.add(panelRobotCommands);
255
256 333 gtress
257 32 gtress
                // Task Manager
258
                panelTaskManager = new JPanel();
259
                panelTaskManager.setLayout(new BorderLayout());
260
                taskListModel = new DefaultListModel();
261
                taskList = new JList(taskListModel);
262
                taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
263
                taskList.setSelectedIndex(0);
264
                spTaskManager = new JScrollPane(taskList);
265
                panelTaskManagerControls = new JPanel();
266 320 gtress
                panelTaskManagerControls.setLayout(new GridLayout(1,4));
267 32 gtress
                panelTaskManagerControlsPriority = new JPanel();
268
                panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
269
                btnAddTask = new JButton("Add...");
270
                btnRemoveTask = new JButton("Remove");
271
                btnMoveTaskUp = new JButton("^");
272
                btnMoveTaskDown = new JButton("v");
273 320 gtress
                btnUpdateTasks = new JButton("Update");
274 32 gtress
                panelTaskManagerControlsPriority.add(btnMoveTaskUp);
275
                panelTaskManagerControlsPriority.add(btnMoveTaskDown);
276
                panelTaskManagerControls.add(btnAddTask);
277
                panelTaskManagerControls.add(btnRemoveTask);
278 320 gtress
                panelTaskManagerControls.add(btnUpdateTasks);
279 32 gtress
                panelTaskManagerControls.add(panelTaskManagerControlsPriority);
280
                panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
281
                panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
282
                panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
283 320 gtress
                taskAddWindow = new TaskAddWindow();
284 136 gtress
285 32 gtress
                // Message log
286
                log = new JTextArea();
287
                spLog = new JScrollPane(log,
288
                        ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
289
                        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
290
                spLog.setBorder(BorderFactory.createTitledBorder("Log"));
291 320 gtress
                spLog.setPreferredSize(new Dimension(0, 120));
292 32 gtress
                log.setEditable(false);
293
294
                // Main control mechanism
295
                panelControl = new JPanel();
296
                panelControl.setLayout(new GridLayout(1,1));
297
                tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
298 341 gtress
                tabPaneControl.setPreferredSize(new Dimension(VECTOR_CONTROLLER_WIDTH, 0));
299 32 gtress
                tabPaneControl.addTab("Connection", panelServerInterface);
300
                tabPaneControl.addTab("Robots", panelRobotControl);
301
                tabPaneControl.addTab("Tasks", panelTaskManager);
302
                panelControl.add(tabPaneControl);
303
304
                // Set up elements in the south
305
                panelSouth = new JPanel();
306
                panelSouth.setLayout(new GridLayout(1,2));
307 320 gtress
                //panelSouth.add(spLog);
308 32 gtress
309 320 gtress
                // Put all elements in the ContentPane
310 32 gtress
                this.getContentPane().setLayout(new BorderLayout());
311 320 gtress
                this.getContentPane().add(tabPaneMain, BorderLayout.CENTER);
312 32 gtress
                this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
313
                this.getContentPane().add(panelControl, BorderLayout.EAST);
314
                this.setVisible(true);
315
316 320 gtress
                /* Add all listeners here */
317
                // Task Management
318 32 gtress
                btnAddTask.addActionListener(this);
319
                btnRemoveTask.addActionListener(this);
320
                btnMoveTaskUp.addActionListener(this);
321
                btnMoveTaskDown.addActionListener(this);
322 320 gtress
                btnUpdateTasks.addActionListener(this);
323
                // Robot Control
324
                btnF.addActionListener(this);
325
                btnB.addActionListener(this);
326
                btnL.addActionListener(this);
327
                btnR.addActionListener(this);
328
                btnF.addKeyListener(this);
329
                btnB.addKeyListener(this);
330
                btnL.addKeyListener(this);
331
                btnR.addKeyListener(this);
332
                btnActivate.addActionListener(this);
333
                btnActivate.addKeyListener(this);
334
                cmbRobotNum.addKeyListener(this);
335 428 gtress
                btnCommand_MoveTo.addActionListener(this);
336 429 gtress
                btnCommand_MoveAll.addActionListener(this);
337 320 gtress
                btnCommand_StopTask.addActionListener(this);
338
                btnCommand_ResumeTask.addActionListener(this);
339
                btnCommand_ChargeNow.addActionListener(this);
340
                btnCommand_StopCharging.addActionListener(this);
341
                // Other
342 32 gtress
                btnConnect.addActionListener(this);
343 414 gtress
                btnGetXBeeIDs.addActionListener(this);
344 427 gtress
                btnAssignID.addActionListener(this);
345
                panelWebcam.addMouseListener(this);
346 320 gtress
347 420 gtress
                // Set up animation threads
348 32 gtress
                indicator = new SelectionIndicator(canvas);
349
                indicator.setRadius(RADIUS+3, 15);  //a tad more than the bot radius
350
351
        }
352
353 35 gtress
        public void run () {
354
                while (true) {
355 32 gtress
                        repaint();
356 35 gtress
                        try {
357
                                Thread.sleep(90);
358
                        } catch (InterruptedException e) {
359
                                return;
360
                        }
361 32 gtress
                }
362
        }
363 428 gtress
364
        public void paint (Graphics g) {
365
            super.paint(g);
366
        }
367 429 gtress
368
        public void update (Graphics g) {
369
            paint(g);
370
        }
371 32 gtress
372 333 gtress
        /**
373
        * Gets the JTextArea used for storing the activity log. This method returns a reference to the
374
        * JTextArea that stores the log. The log can contain any activity that is revelant to the use
375
        * of the applet, and may optionally display debugging information.
376
        *
377
        * @return the JTextArea where BOM matrix information is stored.
378
        */
379 320 gtress
        public JTextArea getLog () {
380
                return log;
381
        }
382
383 333 gtress
        /**
384
        * Gets the JTextArea used for storing the BOM matrix data. This method returns a reference to the
385
        * JTextArea that stores the BOM matrix. The values in the matrix are stored as integers separated
386
        * by spaces, and the lines should be separated by a newline.
387
        *
388
        * @return the JTextArea where BOM matrix information is stored.
389
        */
390 320 gtress
        public JTextArea getMatrixInput () {
391
                return txtMatrix;
392
        }
393
394 333 gtress
        /**
395
        * Parses a String containing BOM matrix information.
396
        * The ColonetServerInterface receives lines of the BOM matrix.  (For encoding
397
        * information, see the ColonetServerInterface documentation.)  The entire matrix is passed
398
        * to the client when requested. This method takes a string of the form
399
        * "[command code] [command code] [number of robots] [data0] [data1] ..."
400
        * with tokens separated by spaces and containing no brackets.
401
        * The [command code]s are predefined values identifying this String as a BOM data
402
        * String, [number of robots] is an integer, and the values that follow are
403
        * the sensor readings of the robots in order, starting with robot 0.  Only [number of robots]^2
404
        * data entries will be read.  The matrix values are saved locally until the next String is parsed.
405
        *
406
        *
407
        * @param line the String containing BOM matrix information.
408
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots]^2 data entries in the String
409
        */
410 320 gtress
        public void parseMatrix (String line) {
411
                txtMatrix.setText("");
412
                String [] str = line.split(" ");
413
                int num = Integer.parseInt(str[2]);
414
                for (int i = 0; i < num; i++) {
415
                        for (int j = 0; j < num; j++) {
416
                                String next = str[3 + i*num + j];
417
                                if (next.equals("-1"))
418
                                        txtMatrix.append("-");
419
                                else
420
                                        txtMatrix.append(next);
421
                                if (j < num - 1)
422
                                        txtMatrix.append(" ");
423
                        }
424
                        if (i < num - 1)
425
                                txtMatrix.append("\n");
426 136 gtress
                }
427
428
        }
429
430 420 gtress
        public void connect () {
431
            webcamLoader = new WebcamLoader(this);
432
                dataUpdater = new DataUpdater();
433 428 gtress
                paintThread = new Thread(this, "paintThread");
434 420 gtress
                csi = new ColonetServerInterface(this);
435
                csi.connect(txtHost.getText(), txtPort.getText());
436
                if (!csi.isReady())
437
                        return;
438
                btnConnect.setEnabled(false);
439
                lblConnectionStatus.setText("Status: Connected");
440 428 gtress
                paintThread.start();
441 420 gtress
                dataUpdater.start();
442
                webcamLoader.start();
443
        }
444
445
        public void disconnect () {
446
            btnConnect.setEnabled(true);
447
            lblConnectionStatus.setText("Status: Disconnected");
448 428 gtress
            try { paintThread.interrupt(); } catch (Exception e) { }
449 420 gtress
                try { indicator.interrupt(); } catch (Exception e) { }
450
451
        }
452
453 333 gtress
        /**
454
        * Parses a String containing a task queue update.
455
        * Format is currently not specified.
456
        * This method currently does nothing.
457
        *
458
        * @param line the String containing task queue update information.
459
        */
460 320 gtress
        public void parseQueue (String line) {
461
                log.append("Got queue update\n");
462 333 gtress
                //TODO: display new queue data in tasks tab
463 320 gtress
        }
464
465 333 gtress
        /**
466
        * Parses a String containing XBee ID values.
467
        * The ColonetServerInterface receives Strings of XBee information.  (For encoding
468
        * information, see the ColonetServerInterface documentation.)  This method takes
469
        * a string of the form "[command code] [command code] [number of robots] [id0] [id1] ..."
470
        * with tokens separated by spaces and containing no brackets.
471
        * The [command code]s are predefined values identifying this String as an XBee
472
        * ID String, [number of robots] is an integer, and the values that follow are
473
        * the IDs of the robots in order, starting with robot 0.  Only [number of robots]
474
        * will be read.  The ID values are saved locally until the next String is parsed.
475
        * The purpose of having this list is to ensure that robots are properly identified for control purposes.
476
        * This keeps robot identification consistent between sessions and prevents arbitrary assignment.
477
        *
478
        * @param line the String containing XBee ID information.
479
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots] IDs in the String
480
        * @see ColonetServerInterface#sendXBeeIDRequest()
481
        */
482 320 gtress
        public void parseXBeeIDs (String line) {
483 333 gtress
484 320 gtress
                String [] str = line.split(" ");
485
                int num = Integer.parseInt(str[2]);
486
                xbeeID = new int[num];
487
                for (int i = 0; i < num; i++)
488
                        xbeeID[i] = Integer.parseInt(str[i+3]);
489
490
                //update the list of robots to control
491
                //but save the old value first
492
                Object oldSelection = cmbRobotNum.getSelectedItem();
493
                cmbRobotNum.removeAllItems();
494
                cmbRobotNum.addItem(new String("   All   "));
495
                for (int i = 0; i < num; i++)
496
                        cmbRobotNum.addItem(new String("" + xbeeID[i]));
497
                cmbRobotNum.setSelectedItem(oldSelection);
498
        }
499
500 333 gtress
        /**
501
        * Parses a String containing battery information.
502
        * The ColonetServerInterface receives Strings of battery information.  (For encoding
503
        * information, see the ColonetServerInterface documentation.)  This method takes
504
        * a string of the form "[command code] [command code] [robot ID] [value]"
505
        * with tokens separated by spaces and containing no brackets.
506
        * The [command code]s are predefined values identifying this String as a battery
507
        * information String, [robot ID] is an integer, and [value] is a battery measurement.
508
        * This updates the batery information for a single robot.
509
        *
510
        *
511
        * @param line the String containing battery information.
512
        * @see ColonetServerInterface#sendBatteryRequest(int)
513
        */
514 320 gtress
        public void parseBattery (String line) {
515
                String [] str = line.split(" ");
516 425 gtress
                int botNum = Integer.parseInt(str[2]);
517
                int level = Integer.parseInt(str[3]);
518 428 gtress
                int selected = -1;
519 425 gtress
                try {
520 428 gtress
                    selected = Integer.parseInt((String)cmbRobotNum.getSelectedItem());
521 425 gtress
                } catch (Exception e) {
522 333 gtress
                }
523 428 gtress
                if (selected == botNum) {
524 425 gtress
                        batteryIcon.setLevel(level);
525
                }
526 320 gtress
        }
527
528 32 gtress
        //
529 320 gtress
        // MouseListener methods
530 32 gtress
        //
531 320 gtress
        public void mouseExited(MouseEvent e) {
532
        }
533
        public void mouseEntered(MouseEvent e) {
534
        }
535
        public void mouseReleased(MouseEvent e) {
536
        }
537
        public void mouseClicked(MouseEvent e) {
538
        }
539 35 gtress
        public void mousePressed(MouseEvent e) {
540 428 gtress
541
            // If we are selecting a waypoint (destination) for a specific bot
542 429 gtress
            if (setWaypoint && setWaypointID  >= 0) {
543
                setWaypoint = false;
544
                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
545 428 gtress
                if (selectedBot < 0 || robotIcons.get(selectedBot).id < 0)
546
                    return;
547
548
                RobotIcon r = robotIcons.get(selectedBot);
549
                r.destx = e.getX();
550
                r.desty = e.getY();
551 429 gtress
                return;
552
            }
553
554
            // If we are setting all waypoints
555
            if (setWaypoint) {
556 428 gtress
                setWaypoint = false;
557
                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
558 429 gtress
                for (int i = 0; i < robotIcons.size(); i++) {
559
                    RobotIcon r = robotIcons.get(i);
560
                    if (r.id < 0)
561
                        continue;
562
                    r.destx = e.getX();
563
                    r.desty = e.getY();
564
                }
565 428 gtress
                return;
566
            }
567
568
            // Otherwise, we are selecting a bot, or doing nothing
569
                for (int i = 0; i < robotIcons.size(); i++) {
570
                    RobotIcon r = robotIcons.get(i);
571
                    if (r.contains(e.getX(), e.getY())) {
572
                        selectedBot = i;
573
                        if (r.id < 0)
574
                            lblSelected.setText("Unidentified");
575
                        else
576
                            lblSelected.setText(" " + r.id);
577
                    }
578
                }
579
                repaint();
580 320 gtress
        }
581
        public void mouseDragged(MouseEvent e) {
582
        }
583
        public void mouseMoved(MouseEvent e) {
584
        }
585 32 gtress
586 320 gtress
        //
587
        // KeyListener methods
588
        //
589
        public void keyPressed (KeyEvent e) {
590
                int code = e.getKeyCode();
591
                if (code == KeyEvent.VK_UP) {
592
                        vectorController.setMaxForward();
593
                        vectorController.sendToServer();
594
                } else if (code == KeyEvent.VK_DOWN) {
595
                        vectorController.setMaxReverse();
596
                        vectorController.sendToServer();
597
                } else if (code == KeyEvent.VK_LEFT) {
598
                        vectorController.setMaxLeft();
599
                        vectorController.sendToServer();
600
                } else if (code == KeyEvent.VK_RIGHT) {
601
                        vectorController.setMaxRight();
602
                        vectorController.sendToServer();
603
                } else if (code == KeyEvent.VK_S) {
604
                        vectorController.setZero();
605
                        vectorController.sendToServer();
606
                }
607 181 gtress
        }
608 320 gtress
        public void keyReleased (KeyEvent e) {
609
        }
610
        public void keyTyped (KeyEvent e) {
611
        }
612 136 gtress
613 320 gtress
614
        //
615
        // ActionListener method
616
        //
617
        public void actionPerformed (ActionEvent e) {
618
                Object source = e.getSource();
619 414 gtress
620 427 gtress
                // General Actions
621 404 gtress
                if (source == btnConnect) {
622 420 gtress
                        connect();
623 414 gtress
                } else if (source == btnGetXBeeIDs) {
624
                        csi.sendXBeeIDRequest();
625 427 gtress
                } else if (source == btnAssignID) {
626
                    String message;
627 428 gtress
                    if (selectedBot < 0)
628
                        return;
629
                    int curID = robotIcons.get(selectedBot).id;
630
                    if (curID < 0)
631
                        message = "That robot is unidentified. Please specify its ID.";
632
                    else
633
                        message = "That robot has ID " + curID + ". You may reassign it now.";
634
                    String result = JOptionPane.showInputDialog(this, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
635
                    if (result == null)
636
                        return;
637
                int newID = -1;
638
                    try {
639
                        newID = Integer.parseInt(result);
640
                    } catch (Exception ex) {
641
                        csi.warn("Invalid ID.");
642
                        return;
643
                    }
644
                    // Assign new ID and update display
645
                    robotIcons.get(selectedBot).id = newID;
646
                    robotIcons.get(selectedBot).color = Color.GREEN;
647
                    lblSelected.setText(" " + newID);
648
649 427 gtress
650 320 gtress
                }
651 414 gtress
652 320 gtress
                // Robot Movement Controls
653
                else if (source == btnF) {
654
                        vectorController.setMaxForward();
655
                        vectorController.sendToServer();
656
                } else if (source == btnB) {
657
                        vectorController.setMaxReverse();
658
                        vectorController.sendToServer();
659
                } else if (source == btnL) {
660
                        vectorController.setMaxLeft();
661
                        vectorController.sendToServer();
662
                } else if (source == btnR) {
663
                        vectorController.setMaxRight();
664
                        vectorController.sendToServer();
665
                } else if (source == btnActivate) {
666
                        vectorController.setZero();
667
                        vectorController.sendToServer();
668
                }
669
                // Robot Commands (non-movement)
670 428 gtress
                else if (source == btnCommand_MoveTo) {
671 429 gtress
                    if (selectedBot < 0)
672
                        return;
673 428 gtress
                    panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
674
                    setWaypoint = true;
675 429 gtress
                    setWaypointID = selectedBot;
676 428 gtress
677 429 gtress
                } else if (source == btnCommand_MoveAll) {
678
                    panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
679
                    setWaypoint = true;
680
                    setWaypointID = -1;
681
682 428 gtress
                } else if (source == btnCommand_StopTask) {
683 320 gtress
684
                } else if (source == btnCommand_ResumeTask) {
685
686
                } else if (source == btnCommand_ChargeNow) {
687
688
                } else if (source == btnCommand_StopCharging) {
689
690
                }
691
692
                // Queue Management
693
                else if (source == btnAddTask) {
694
                        taskAddWindow.prompt();
695
                } else if (source == btnRemoveTask) {
696
                        if (taskList.getSelectedIndex() >= 0);
697
                                csi.sendQueueRemove(taskList.getSelectedIndex());
698
                        csi.sendQueueUpdate();
699
                } else if (source == btnMoveTaskUp) {
700
                        csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
701
                        csi.sendQueueUpdate();
702
                } else if (source == btnMoveTaskDown) {
703
                        csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
704
                        csi.sendQueueUpdate();
705
                } else if (source == btnUpdateTasks) {
706
                        csi.sendQueueUpdate();
707
                }
708
        }
709
710 32 gtress
        /*
711
        *        SelectionIndicator thread.
712
        *        Graphical representation of the selection marker
713
        *
714
        *        step() and draw() are synchronized methods. step() is private and
715
        *        used to update the position of the crosshairs. draw() is called
716
        *        externally and should only run if all calculations in step() have
717
        *        been completed.
718
        */
719 35 gtress
        private class SelectionIndicator extends Thread {
720 32 gtress
721 320 gtress
                final int INDICATOR_DELAY = 180;
722
                final double DTHETA = 0.4;    //larger values make the marker rotate faster
723 32 gtress
                Graphics2D g;   //canvas to draw on
724
                boolean running;
725
726
                int sx, sy;                //center
727
                int r, dr;                //radius and width of marker
728
                double theta;   //current angle
729
730
                volatile Polygon poly1, poly2, poly3, poly4;
731
732
                int px1, py1;
733
                int rx1, ry1;
734
                int px2, py2;
735
                int rx2, ry2;
736
                int px3, py3;
737
                int rx3, ry3;
738
                int px4, py4;
739
                int rx4, ry4;
740
741
                int steps;
742
743 35 gtress
                public SelectionIndicator (Graphics2D g) {
744 32 gtress
                        super("SelectionIndicator");
745
                        this.g = g;
746
                        running = false;
747
                        steps = 0;
748
749
                        theta = 0;
750
                        rx1 = 0; ry1 = 0;
751
                        px1 = 0; py1 = 0;
752
                        rx2 = 0; ry2 = 0;
753
                        px2 = 0; py2 = 0;
754
                        rx3 = 0; ry3 = 0;
755
                        px3 = 0; py3 = 0;
756
                        rx4 = 0; ry4 = 0;
757
                        px4 = 0; py4 = 0;
758
                }
759
760 35 gtress
                public synchronized void setCenter (int sx, int sy) {
761 32 gtress
                        if (sx == this.sx && sy == this.sy) return;
762
                        this.sx = sx;
763
                        this.sy = sy;
764
                        steps = 0;
765
                }
766
767 35 gtress
                public synchronized void setRadius (int r, int dr) {
768 32 gtress
                        this.r = r;
769
                        this.dr = dr;
770
                        steps = 0;
771
                }
772
773 35 gtress
                public void run () {
774 32 gtress
                        running = true;
775 35 gtress
                        while (running) {
776 32 gtress
                                step();
777 35 gtress
                                try {
778
                                        Thread.sleep(INDICATOR_DELAY);
779
                                } catch (InterruptedException e) {
780
                                        running = false;
781
                                        return;
782
                                }
783 32 gtress
                        }
784
                }
785
786 35 gtress
                private synchronized void step () {
787 32 gtress
                        Polygon poly1_new = new Polygon();
788
                        Polygon poly2_new = new Polygon();
789
                        Polygon poly3_new = new Polygon();
790
                        Polygon poly4_new = new Polygon();
791
792
                        //the step
793
                        theta = (theta + DTHETA/Math.PI) % (Math.PI);
794
795
                        //the calculation
796
                        //let p be the point of the pointy thing toward the center
797
                        //let r be the point at the opposite side
798
799
                        //recalculate radius, if it will look cool, lolz
800
                        int newr = r;
801 38 gtress
                        if (steps < 100)
802 320 gtress
                        newr = (int)( r + 200/(steps+1) );
803 32 gtress
804
                        //precompute values for dx and dy
805
                        int dx_inner = (int)(newr * Math.cos(theta));
806
                        int dy_inner = (int)(newr * Math.sin(theta));
807
                        int dx_outer = (int)((newr+dr) * Math.cos(theta));
808
                        int dy_outer = (int)((newr+dr) * Math.sin(theta));
809
810
                        //calculate polygon constants
811
                        int dy_poly = (int)(dr/2 * Math.cos(theta));
812
                        int dx_poly = (int)(dr/2 * Math.sin(theta));
813
814
                        //determine critical points
815 35 gtress
                        //kansas city shuffle!
816 32 gtress
                        px1 = sx + dx_inner;
817
                        py1 = sy - dy_inner;
818
                        rx1 = sx + dx_outer;
819
                        ry1 = sy - dy_outer;
820
                        px2 = sx - dx_inner;
821
                        py2 = sy + dy_inner;
822
                        rx2 = sx - dx_outer;
823
                        ry2 = sy + dy_outer;
824
                        px3 = sx - dy_inner;
825
                        py3 = sy - dx_inner;
826
                        rx3 = sx - dy_outer;
827
                        ry3 = sy - dx_outer;
828
                        px4 = sx + dy_inner;
829
                        py4 = sy + dx_inner;
830
                        rx4 = sx + dy_outer;
831
                        ry4 = sy + dx_outer;
832
833
                        //create polygons
834
                        poly1_new.addPoint(px1, py1);
835
                        poly1_new.addPoint(rx1+dx_poly, ry1+dy_poly);
836
                        poly1_new.addPoint(rx1-dx_poly, ry1-dy_poly);
837
                        poly2_new.addPoint(px2, py2);
838
                        poly2_new.addPoint(rx2+dx_poly, ry2+dy_poly);
839
                        poly2_new.addPoint(rx2-dx_poly, ry2-dy_poly);
840
                        poly3_new.addPoint(px3, py3);
841
                        poly3_new.addPoint(rx3-dy_poly, ry3+dx_poly);
842
                        poly3_new.addPoint(rx3+dy_poly, ry3-dx_poly);
843
                        poly4_new.addPoint(px4, py4);
844
                        poly4_new.addPoint(rx4-dy_poly, ry4+dx_poly);
845
                        poly4_new.addPoint(rx4+dy_poly, ry4-dx_poly);
846
847
                        //reassign updated polygons
848
                        poly1 = poly1_new;
849
                        poly2 = poly2_new;
850
                        poly3 = poly3_new;
851
                        poly4 = poly4_new;
852
853
                        if (steps < 300) steps++;
854
                }
855
856 35 gtress
                public synchronized void draw () {
857 32 gtress
                        if (!running) return;
858
                        g.setColor(Color.GRAY);
859
                        //draw polygons
860
                        g.fillPolygon(poly1);
861
                        g.fillPolygon(poly2);
862
                        g.fillPolygon(poly3);
863
                        g.fillPolygon(poly4);
864
                }
865
866
        }
867
868
        /*
869 320 gtress
        *        DataUpdater thread.
870
        *   The purpose of this thread is to request data from the server at regular intervals.
871 32 gtress
        *
872
        */
873 320 gtress
        class DataUpdater extends Thread {
874 425 gtress
                final int DATAUPDATER_DELAY = 2200;
875 320 gtress
876
                public DataUpdater () {
877
                        super("Colonet DataUpdater");
878
                }
879
880
                public void run () {
881
                        String line;
882
                        while (true) {
883
                                try {
884
                                        //request more data
885
                                        if (csi != null && csi.isReady()) {
886 333 gtress
                                                //csi.sendSensorDataRequest();
887 425 gtress
                                                csi.sendXBeeIDRequest();
888
                                                if (cmbRobotNum.getSelectedIndex() > 0) {
889
                                                    String sel = (String) cmbRobotNum.getSelectedItem();
890
                                                    int num = Integer.parseInt(sel);
891
                                                        csi.sendBatteryRequest(num);
892
                                                } else {
893 333 gtress
                                                        csi.sendBatteryRequest(200);
894 425 gtress
                                                }
895 320 gtress
                                        }
896
                                        Thread.sleep(DATAUPDATER_DELAY);
897
                                } catch (InterruptedException e) {
898
                                        return;
899
                                }
900
                        }
901
                }
902
903
        }
904 136 gtress
905 320 gtress
        /*
906
        *        GraphicsPanel class
907 341 gtress
        *        An extension of JPanel, designed for holding an image that will be repainted regularly.
908 320 gtress
        */
909
        class GraphicsPanel extends JPanel {
910
                protected Image img;
911 136 gtress
912 320 gtress
                public GraphicsPanel (Image img) {
913
                        super();
914
                        this.img = img;
915 136 gtress
                }
916
917 341 gtress
                public GraphicsPanel (Image img, boolean isDoubleBuffered) {
918 320 gtress
                        super(isDoubleBuffered);
919
                        this.img = img;
920
                }
921
922
                public void paint (Graphics g) {
923
                        // Place the buffered image on the screen, inside the panel
924
                        g.drawImage(img, 0, 0, Color.WHITE, this);
925
                }
926
927
        }
928
929
        /*
930
        *        WebcamPanel class
931
        *        Enables more efficient image handling in a component-controlled environment
932
        */
933
        class WebcamPanel extends JPanel {
934
                int BORDER = 16;  // this is arbitrary. it makes the image look nice inside a border.
935
                int BOT_RADIUS = 40;
936
                volatile BufferedImage img;
937
938
                public WebcamPanel () {
939
                        super();
940
                }
941
942
                public synchronized void setImage (BufferedImage newimg) {
943
                        if (img != null) {
944
                                img.flush();
945
                                img = null;
946
                                img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
947 429 gtress
                                txtMatrix.append("c");
948 273 gtress
                        }
949 429 gtress
                        System.gc();
950 320 gtress
                        img = newimg;
951 270 gtress
                }
952
953 320 gtress
                public synchronized void setPoints (Point [] newpoints) {
954 427 gtress
                        // test code -- need to remove later
955
                        if (newpoints == null) {
956
                                Point [] p = new Point [3];
957 428 gtress
                                p[0] = new Point(200,200);
958
                                p[1] = new Point(100,100);
959 427 gtress
                                p[2] = new Point(400,300);
960
                                setPoints(p);
961
                                return;
962
                        }
963
964
                        for (int i = 0; i < newpoints.length; i++) {
965
                                Point p = newpoints[i];
966
                                boolean found = false;
967
                                for (int j = 0; j < robotIcons.size(); j++) {
968 428 gtress
                                        RobotIcon r = robotIcons.get(j);
969 427 gtress
                                        if (r.isClose(p.x, p.y)) {
970
                                                r.move(p.x, p.y);
971
                                                found = true;
972
                                        }
973
                                }
974
                                if (!found) {
975
                                        RobotIcon r = new RobotIcon(p.x, p.y);
976
                                        robotIcons.add(r);
977
                                }
978
                        }
979
980 273 gtress
                }
981
982 320 gtress
                public synchronized void paint (Graphics g) {
983
984
                        if (img == null)
985
                                return;
986
                        // Place the image on the screen, inside the panel
987
                        g.drawImage(img,
988
                                                BORDER,        //dx1 - the x coordinate of the first corner of the destination rectangle.
989
                                                BORDER,        //dy1 - the y coordinate of the first corner of the destination rectangle.
990
                                                this.getWidth() - BORDER,         //dx2 - the x coordinate of the second corner of the destination rectangle.
991
                                                this.getHeight() - BORDER,        //dy2 - the y coordinate of the second corner of the destination rectangle.
992
                                                0,        //sx1 - the x coordinate of the first corner of the source rectangle.
993
                                                0,        //sy1 - the y coordinate of the first corner of the source rectangle.
994
                                                image.getWidth(),        //sx2 - the x coordinate of the second corner of the source rectangle.
995
                                                image.getHeight(),        //sy2 - the y coordinate of the second corner of the source rectangle.
996
                                                null        //observer - object to be notified as more of the image is scaled and converted.
997
                                                );
998
999
                        // Draw Identifiers
1000 427 gtress
                        if (robotIcons == null)
1001 320 gtress
                                return;
1002
                        ((Graphics2D)g).setStroke(new BasicStroke(2));
1003 427 gtress
                        for (int i = 0; i < robotIcons.size(); i++) {
1004
                                RobotIcon r = robotIcons.get(i);
1005
                                g.setColor(r.color);
1006 428 gtress
                                g.drawOval(r.x-RADIUS, r.y-RADIUS, 2*r.RADIUS, 2*r.RADIUS);
1007
                                // If the robot has a destination, draw the vector
1008
                                if (r.destx >= 0) {
1009
                                    g.drawLine(r.x, r.y, r.destx, r.desty);
1010
                                }
1011 320 gtress
                        }
1012
1013 428 gtress
                        // Identify currently-selected robot
1014
                        if (selectedBot == -1)
1015
                            return;
1016
                        g.setColor(Color.YELLOW);
1017
                        RobotIcon r = robotIcons.get(selectedBot);
1018
                        g.drawOval(r.x-RADIUS-6, r.y-RADIUS-6, 2*r.RADIUS+12, 2*r.RADIUS+12);
1019
1020 270 gtress
                }
1021
1022
        }
1023
1024
        /*
1025 320 gtress
        *        WebcamLoader class
1026
        *        Handles the loading of the webcam image.
1027 270 gtress
        */
1028 320 gtress
        class WebcamLoader extends Thread
1029
        {
1030
                final int WEBCAMLOADER_DELAY = 1000;
1031
                final String IMAGE_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet.jpg";
1032
                final String LOCATIONS_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet/locations.txt";
1033 270 gtress
1034 320 gtress
                URL imagePath;
1035
                URI locationsPath;
1036
1037
                MediaTracker mt;
1038
                BufferedImage image;
1039
1040
                public WebcamLoader (JApplet applet)
1041
                {
1042
                        super("ColonetWebcamLoader");
1043
                        mt = new MediaTracker(applet);
1044
                        ImageIO.setUseCache(false);
1045
                        try {
1046
                                imagePath = new URL(IMAGE_PATH);
1047
                        } catch (MalformedURLException e) {
1048
                                System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n");
1049
                        }
1050
                        try {
1051
                                locationsPath = new URI(LOCATIONS_PATH);
1052
                        } catch (URISyntaxException x) {
1053
                                System.out.println("Malformed URI: could not form URI from: [" + LOCATIONS_PATH + "]\n");
1054
                        }
1055
1056 270 gtress
                }
1057
1058 320 gtress
                public synchronized void run ()
1059
                {
1060 270 gtress
                        while (true) {
1061
                                try {
1062 320 gtress
                                        Thread.sleep(WEBCAMLOADER_DELAY);
1063
                                        if (image != null)
1064
                                                image.flush();
1065 429 gtress
                                        System.gc();
1066 320 gtress
                                        image = ImageIO.read(imagePath);
1067
                                        // The MediaTracker waitForID pauses the thread until the image is loaded.
1068
                                        // We don't want to display a half-downloaded image.
1069
                                        mt.addImage(image, 1);
1070
                                        mt.waitForID(1);
1071
                                        mt.removeImage(image);
1072
                                        // Save
1073
                                        panelWebcam.setImage(image);
1074 429 gtress
                                        parseLocations(locationsPath.toURL());
1075
                                        //panelWebcam.setPoints(null);  //temporary simulation code
1076 270 gtress
                                } catch (InterruptedException e) {
1077
                                        return;
1078 320 gtress
                                } catch (java.security.AccessControlException e) {
1079 389 gtress
                                        csi.warn("Could not load webcam.\n" + e);
1080 320 gtress
                                        return;
1081
                                } catch (IOException e) {
1082
                                        log.append("IOException while trying to load image.");
1083
                                }
1084 270 gtress
                        }
1085
                }
1086 320 gtress
1087
                private void parseLocations (URL url) {
1088
                        URLConnection conn = null;
1089
                        DataInputStream data = null;
1090
                        String line;
1091
                        String [] lines = new String[30];
1092
                        StringBuffer buf = new StringBuffer();
1093
                        int i = 0;
1094
1095
                        try {
1096
                                conn = url.openConnection();
1097
                                conn.connect();
1098
                                data = new DataInputStream(new BufferedInputStream(conn.getInputStream()));
1099
                                while ((line = data.readLine()) != null) {
1100
                                        buf.append(line + ";");
1101
                                        lines[i] = line;
1102
                                        i++;
1103
                                }
1104
                                data.close();
1105
                        } catch (IOException e) {
1106
                                System.out.println("IOException:" + e.getMessage());
1107
                        }
1108
                        //log.append("Got robot locations: " + buf + "\n");
1109
1110
                        // Get Point values from strings
1111
                        Point [] points = new Point[i];
1112
                        for (int j = 0; j < i; j++) {
1113
                                String [] parts = lines[j].split(",");
1114
                                int xval = Integer.parseInt(parts[0]);
1115
                                int yval = Integer.parseInt(parts[1]);
1116
                                Point p = new Point(xval, yval);
1117
                                points[j] = p;
1118
                        }
1119
                        if (points.length != 0)
1120
                                panelWebcam.setPoints(points);
1121
1122
                }
1123
        }
1124 427 gtress
1125
        /*
1126
        *  RobotIcon class
1127
        *  Provides a means for graphically representing and keeping track of webcam bots.
1128
        */
1129
        class RobotIcon {
1130
                public final int RADIUS = 30;
1131 429 gtress
                public final int CLOSE = 70;
1132 427 gtress
1133
                public int x, y;
1134 428 gtress
                public int destx, desty;
1135 427 gtress
                public int id;
1136
                public Color color;
1137
1138
                public RobotIcon (int x, int y) {
1139
                        this.id = -1;
1140
                        this.color = Color.RED;
1141
                        this.x = x;
1142
                        this.y = y;
1143 428 gtress
                        this.destx = -1;
1144
                        this.desty = -1;
1145 427 gtress
                }
1146
1147 428 gtress
                /**
1148
                *  Relocates this RobotIcon to a new coordinate point.
1149
                *
1150
                */
1151 427 gtress
                public void move (int newX, int newY) {
1152
                        this.x = newX;
1153
                        this.y = newY;
1154
                }
1155
1156 428 gtress
                /**
1157
                *  Determines if a given point is within a reasonable range of the current location
1158
                *  to be considered the same robot when moving. The threshold is determined by the
1159
                *  CLOSE value.
1160
                *
1161
                *  @returns Whether or not the given point is reasonably close to the current location.
1162
                *
1163
                */
1164 427 gtress
                public boolean isClose (int nx, int ny) {
1165
                        int dist = (int) Point.distance(this.x, this.y, nx, ny);
1166
                        return (dist < CLOSE);
1167
                }
1168
1169 428 gtress
                /**
1170
                *  Determines whether a given point is within the rectangle that circumscribes the
1171
                *  robot's circlular icon. Used for clicking on robots in webcam view.
1172
        *
1173
        */
1174
                public boolean contains (int px, int py) {
1175
                    Rectangle rect = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS);
1176
                    return rect.contains(px, py);
1177
                }
1178
1179 427 gtress
                public String toString () {
1180
                        String s = "RobotIcon at (" + x + "," + y + "), id " + id;
1181
                        return s;
1182
                }
1183
1184
        }
1185 311 emullini
1186 320 gtress
1187
        /*
1188
        *        VectorController class
1189
        *        Manages robot motion control graphically
1190
        */
1191 427 gtress
        class VectorController extends GraphicsPanel implements MouseListener, MouseMotionListener {
1192 320 gtress
                int x, y, cx, cy;
1193
                int width, height;
1194
                int side;
1195
1196
                public VectorController (Image img) {
1197
                        super (img);
1198
                        width = img.getWidth(null);
1199
                        height = img.getHeight(null);
1200
                        cx = img.getWidth(null)/2;
1201
                        cy = img.getHeight(null)/2;
1202
                        x = cx;
1203
                        y = cy;
1204
                        if (width < height)
1205
                                side = width;
1206
                        else
1207
                                side = height;
1208 427 gtress
                        this.addMouseListener(this);
1209
                        this.addMouseMotionListener(this);
1210 320 gtress
                }
1211
1212
                public void setPoint (int x, int y) {
1213
                        if (!isValidPoint(x, y))
1214
                                return;
1215
                        this.x = x;
1216
                        this.y = y;
1217
                        repaint();
1218
                }
1219
1220
                public boolean isValidPoint (int x, int y) {
1221
                        double xterm = Math.pow(1.0*(x - cx)/(side/2), 2);
1222
                        double yterm = Math.pow(1.0*(y - cy)/(side/2), 2);
1223
                        return (xterm + yterm <= 1);
1224
                }
1225
1226
                public void notifyMouseEvent (MouseEvent e, boolean send) {
1227
                        if (!isValidPoint(e.getX(), e.getY()))
1228
                                return;
1229
                        vectorController.setPoint(e.getX(), e.getY());
1230
                        vectorController.repaint();
1231
                        if (send)
1232
                                vectorController.sendToServer();
1233
                }
1234
1235 427 gtress
                public void mouseExited(MouseEvent e) {
1236
                }
1237
                public void mouseEntered(MouseEvent e) {
1238
                }
1239
                public void mouseReleased(MouseEvent e) {
1240
                        this.notifyMouseEvent(e, true);
1241
                }
1242
                public void mouseClicked(MouseEvent e) {
1243
                        this.notifyMouseEvent(e, false);
1244
                }
1245
                public void mousePressed(MouseEvent e) {
1246
                }
1247
                public void mouseDragged(MouseEvent e) {
1248
                        vectorController.notifyMouseEvent(e, false);
1249
                }
1250
                public void mouseMoved(MouseEvent e) {
1251
                }
1252
1253 320 gtress
                public int getSpeed () {
1254
                        int dx = x - cx;
1255
                        int dy = y - cy;
1256
                        int v = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) );
1257
                        return v;
1258
                }
1259
1260 341 gtress
                /**
1261
                * Returns the angle of the control vector in positive degrees west of north,
1262
                * or negative degrees east of north, whichever is less than or equal to
1263
                * 180 degrees total.
1264
                */
1265 320 gtress
                public int getAngle () {
1266
                        int dx = x - cx;
1267
                        int dy = cy - y;
1268 341 gtress
                        // find reference angle in radians
1269 320 gtress
                        double theta = Math.atan2(Math.abs(dx), Math.abs(dy));
1270 341 gtress
                        // transform to degrees
1271
                        theta = theta * 180 / Math.PI;
1272
                        // adjust for quadrant
1273
                        if (dx < 0 && dy < 0)
1274
                                theta = 90 + theta;
1275
                        else if (dx < 0 && dy >= 0)
1276
                                theta = 90 - theta;
1277
                        else if (dx >= 0 && dy < 0)
1278
                                theta = -90 - theta;
1279
                        else
1280
                                theta = -90 + theta;
1281 320 gtress
                        return (int) theta;
1282
                }
1283
1284
                public void paint (Graphics g) {
1285
                        g.setColor(Color.BLACK);
1286
                        g.fillRect(0, 0, width, height);
1287
                        ((Graphics2D)g).setStroke(new BasicStroke(1));
1288
                        g.setColor(Color.RED);
1289
                        g.drawOval(cx-side/2, cy-side/2, side, side);
1290
                        ((Graphics2D)g).setStroke(new BasicStroke(2));
1291
                        g.setColor(Color.GREEN);
1292
                        g.drawLine(cx, cy, x, y);
1293
                        g.fillOval(x-3, y-3, 6, 6);
1294
                }
1295
1296
                public void setMaxForward () {
1297
                        setPoint(cx, cy - (side/2) + 1);
1298
                }
1299
1300
                public void setMaxReverse () {
1301
                        setPoint(cx, cy + (side/2) - 1);
1302
                }
1303
1304
                public void setMaxLeft () {
1305
                        setPoint(cx - (side/2) + 1, cy);
1306
                }
1307
1308
                public void setMaxRight () {
1309
                        setPoint(cx + (side/2) - 1, cy);
1310
                }
1311
1312
                public void setZero () {
1313
                        setPoint(cx, cy);
1314
                }
1315
1316
                public void sendToServer () {
1317 341 gtress
                        System.out.println("Attempting to send angle = " + getAngle() + ", speed = " + getSpeed() + "");
1318 320 gtress
                        String dest = ColonetServerInterface.GLOBAL_DEST;
1319 425 gtress
                        if (cmbRobotNum != null && cmbRobotNum.getSelectedIndex() > 0) {
1320
                                dest = (String)cmbRobotNum.getSelectedItem();
1321
                        }
1322 320 gtress
1323
                        if (csi != null) {
1324 417 gtress
                                /*
1325
                                csi.sendData(ColonetServerInterface.MOVE + " " + getSpeed() + " " + getAngle(), dest);
1326
                                */
1327
1328
                                //Directional commands
1329
                                if (x > cx && y == cy) {  //move right
1330
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", dest);
1331
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", dest);
1332
                                } else if (x < cx && y == cy) {  //move left
1333
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", dest);
1334
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", dest);
1335
                                } else if (x == cx && y > cy) {  //move forward
1336
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 225", dest);
1337
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 225", dest);
1338
                                } else if (x == cx && y < cy) {  //move backward
1339
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 225", dest);
1340
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 225", dest);
1341
                                } else if (x == cx && y == cy) {  //stop!
1342
                                        csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 0", dest);
1343
                                        csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 0", dest);
1344 320 gtress
                                }
1345
                        }
1346
                }
1347
1348 270 gtress
        }
1349 320 gtress
1350
        /*
1351
        *        TaskAddWindow class
1352 341 gtress
        *        A window that provides a simple way to add tasks to a task queue.
1353 320 gtress
        */
1354
        class TaskAddWindow extends JFrame implements ActionListener, ListSelectionListener {
1355
                JPanel panelButtons;
1356
                JPanel panelParameters;
1357
                JPanel panelSouth;
1358
                JPanel panelSelection;
1359
                JButton btnSubmit;
1360
                JButton btnCancel;
1361
                DefaultListModel availableListModel;
1362
                JList availableList;
1363
                JScrollPane spAvailableTasks;
1364
                JTextArea txtDescription;
1365
                JTextField txtParameters;
1366
1367
                public TaskAddWindow () {
1368
                        super("Add a Task");
1369
                        super.setSize(500,500);
1370
                        super.setLayout(new BorderLayout());
1371
1372
                        // set up buttons
1373
                        btnSubmit = new JButton("Submit");
1374
                        btnCancel = new JButton("Cancel");
1375
                        panelButtons = new JPanel();
1376
                        panelButtons.setLayout(new FlowLayout());
1377
                        panelButtons.add(btnSubmit);
1378
                        panelButtons.add(btnCancel);
1379
                        this.getRootPane().setDefaultButton(btnSubmit);
1380
1381
                        // set up task list
1382
                        availableListModel = new DefaultListModel();
1383
                        availableListModel.addElement("Map the Environment");
1384
                        availableListModel.addElement("Clean Up Chemical Spill");
1385
                        availableListModel.addElement("Grow Plants");
1386
                        availableListModel.addElement("Save the Cheerleader");
1387
                        availableListModel.addElement("Save the World");
1388
                        availableList = new JList(availableListModel);
1389
                        availableList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1390
                        availableList.setSelectedIndex(-1);
1391
                        spAvailableTasks = new JScrollPane(availableList);
1392
                        spAvailableTasks.setBorder(BorderFactory.createTitledBorder("Select A Task"));
1393
                        txtDescription = new JTextArea();
1394
                        txtDescription.setEditable(false);
1395
                        txtDescription.setLineWrap(true);
1396
                        txtDescription.setWrapStyleWord(true);
1397
                        txtDescription.setBorder(BorderFactory.createTitledBorder("Description"));
1398
1399
                        //set up parameter area
1400
                        panelParameters = new JPanel();
1401
                        panelParameters.setLayout(new BorderLayout());
1402
                        txtParameters = new JTextField();
1403
                        panelParameters.add(new JLabel("Optional parameters for this task: "), BorderLayout.WEST);
1404
                        panelParameters.add(txtParameters);
1405
1406
                        // assemble objects
1407
                        panelSelection = new JPanel();
1408
                        panelSelection.setLayout(new GridLayout(1,2));
1409
                        panelSelection.add(spAvailableTasks);
1410
                        panelSelection.add(txtDescription);
1411
1412
                        panelSouth = new JPanel();
1413
                        panelSouth.setLayout(new GridLayout(2,1));
1414
                        panelSouth.add(panelParameters);
1415
                        panelSouth.add(panelButtons);
1416
1417
                        this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
1418
                        this.getContentPane().add(panelSelection, BorderLayout.CENTER);
1419
                        this.setLocationRelativeTo(null);
1420
1421
                        // add listeners here
1422
                        availableList.addListSelectionListener(this);
1423
                        btnSubmit.addActionListener(this);
1424
                        btnCancel.addActionListener(this);
1425
                }
1426
1427
                public void prompt () {
1428
                        this.setVisible(true);
1429
                }
1430
1431
                private String getDescription (int index) {
1432
                        if (index < 0)
1433
                                return "";
1434
                        switch (index) {
1435
                                case 0: return "SLAM and junk";
1436
                                case 1: return "I'm not sure this works";
1437
                                case 2: return "Push them into the light";
1438 333 gtress
                                case 3: return "...";
1439
                                case 4: return "...";
1440 320 gtress
1441
                                default: return "Task not recognized";
1442
                        }
1443
                }
1444
1445
                public void actionPerformed (ActionEvent e) {
1446
                        Object source = e.getSource();
1447
                        if (source == btnSubmit) {
1448
                                txtParameters.setText(txtParameters.getText().trim());
1449
1450
1451
                                this.setVisible(false);
1452
                        } else if (source == btnCancel) {
1453
                                this.setVisible(false);
1454
                        }
1455
                }
1456
1457
                public void valueChanged (ListSelectionEvent e) {
1458
                        int index = availableList.getSelectedIndex();
1459
                        if (index >= 0)
1460
                                txtDescription.setText(getDescription(index));
1461
                }
1462
1463
        }
1464
1465 427 gtress
        /*
1466
        *  BatteryIcon class
1467
        *  Graphical representation of battery level
1468
        */
1469 341 gtress
        class BatteryIcon implements Icon {
1470 311 emullini
                private int width;
1471
            private int height;
1472
            private int level;
1473
1474 341 gtress
                /**
1475
                * Constructs a new BatteryIcon with all default parameters.
1476
                * Default width and height are 50.
1477
                * Default level is 100.
1478
                */
1479 311 emullini
            public BatteryIcon(){
1480 341 gtress
                    this(100, 50, 50);
1481 311 emullini
            }
1482
1483 341 gtress
                /**
1484
                * Constructs a new BatteryIcon with default width and height, and with the specified level.
1485
                * Default width and height are 50.
1486
                */
1487 311 emullini
            public BatteryIcon(int startLevel){
1488 341 gtress
                    this(startLevel, 50, 50);
1489 311 emullini
            }
1490
1491 341 gtress
                /**
1492
                * Constructs a new BatteryIcon with the specified level, width, and height.
1493
                */
1494 311 emullini
            public BatteryIcon(int startLevel, int w, int h){
1495
                    level = startLevel;
1496
                    width = w;
1497
                    height = h;
1498
            }
1499
1500
            public void paintIcon(Component c, Graphics g, int x, int y) {
1501
                Graphics2D g2d = (Graphics2D) g.create();
1502 341 gtress
                //clear the background
1503 311 emullini
                g2d.setColor(Color.WHITE);
1504 341 gtress
                g2d.fillRect(x + 1, y + 1, width - 2, height - 2);
1505 311 emullini
                //outline
1506
                g2d.setColor(Color.BLACK);
1507 341 gtress
                g2d.drawRect((int)(x + width*.3), y + 2, (int)(width*.4), height - 4);
1508 311 emullini
                //battery life rectangle
1509
                g2d.setColor(Color.GREEN);
1510
                int greenX = (int)(x + 1 + width*.3);
1511
                int greenY = (int)((y+3) + Math.abs(level-100.0)*(height-6)/(100));
1512
                int greenWidth = (int)(width*.4 - 2)+1;
1513
                int greenHeight = 1+(int)(level-0.0)*(height-6)/(100);
1514
                g2d.fillRect(greenX, greenY, greenWidth, greenHeight);
1515
                //text
1516
                g2d.setColor(Color.BLACK);
1517
                g2d.drawString(level + "%", greenX + greenWidth/2 - 10, greenY + greenHeight/2 + 5);
1518
1519
                g2d.dispose();
1520 425 gtress
                System.out.println("Painted icon");
1521 311 emullini
            }
1522
1523 341 gtress
                /**
1524
                * Sets the battery level for this BatteryIcon. The level should be given in raw form, i.e. 0-255 directly
1525
                * from the robot. The value will be converted to a representative percentage automatically.
1526
                *
1527
                * @param newLevel the new battery reading from the robot that this BatteryIcon will display.
1528
                */
1529 333 gtress
            public void setLevel(int newLevel) {
1530 341 gtress
                    level = convert(newLevel);
1531 425 gtress
                    repaint();
1532
                    System.out.println("Updated level to " + level);
1533 311 emullini
            }
1534 341 gtress
1535 311 emullini
            public int getIconWidth() {
1536
                return width;
1537
            }
1538
1539
            public int getIconHeight() {
1540
                return height;
1541
            }
1542 341 gtress
1543
                /**
1544
                * Converts a robot battery reading into representable form.
1545
                * Readings from the robot are returned as raw values, 0-255. This method converts the reading into a value
1546
                * from 0 to 100 so that the practical remaining charge is represented.
1547
                *
1548
                * @param level The battery level as returned by the robot.
1549
                * @returns The representable battery percentage.
1550
                */
1551
                private int convert (int level) {
1552
                        // TODO: make this a forreals conversion.
1553 425 gtress
                        return (int) (100.0 * level / 128);
1554 341 gtress
                }
1555
1556 136 gtress
        }
1557 32 gtress
1558 320 gtress
1559 427 gtress
1560 32 gtress
}