Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / colonet / client / Colonet.java @ 758

History | View | Annotate | Download (37.6 KB)

1 32 gtress
import javax.swing.*;
2 320 gtress
import javax.swing.event.*;
3
import javax.imageio.*;
4 32 gtress
import java.awt.*;
5
import java.awt.image.*;
6
import java.awt.event.*;
7
import java.net.*;
8
import java.io.*;
9 427 gtress
import java.util.*;
10 32 gtress
11 320 gtress
/**
12 531 emarinel
* The Colonet Graphical User Interface Applet for use locally and over an internet connection.
13
* @author Gregory Tress
14 527 emarinel
*
15 531 emarinel
* To generate javadoc on this file or other java files, use javadoc *.java -d doc, where doc
16
* is the name of the folder into which the files should be written.
17 320 gtress
*/
18 671 gtress
public class Colonet extends JApplet implements ActionListener, MouseInputListener, KeyListener {
19 320 gtress
20 32 gtress
        // Connection
21 527 emarinel
        JTextField txtHost;
22
        JTextField txtPort;
23
        JButton btnConnect;
24 414 gtress
        JButton btnGetXBeeIDs;
25 32 gtress
        JLabel lblConnectionStatus;
26 527 emarinel
        JTextArea txtInfo;
27 32 gtress
        JPanel panelConnect;
28
        JPanel panelServerInterface;
29 527 emarinel
        Socket socket;
30
        DataUpdater dataUpdater;
31
32 32 gtress
        // Control
33
        JTabbedPane tabPaneControl;
34
        JPanel panelRobotControl;
35
        JPanel panelRobotDirection;
36 320 gtress
        JPanel panelRobotDirectionButtons;
37 32 gtress
        JPanel panelRobotCommands;
38
        JButton btnF, btnB, btnL, btnR, btnActivate;
39 320 gtress
        JLabel lblBattery;
40 428 gtress
        JLabel lblSelected;
41 333 gtress
        BatteryIcon batteryIcon;
42 320 gtress
        JPanel panelBattery;
43
        VectorController vectorController;
44
        BufferedImage imageVectorControl;
45 427 gtress
        JButton btnAssignID;
46 702 gtress
        JButton btnLocateStation;
47
        boolean setStation;
48
        ChargingStation station;
49 428 gtress
        boolean setWaypoint;
50 429 gtress
        int setWaypointID;
51 739 gtress
        JButton btnSetBounds;
52
        JButton btnClearBounds;
53
        RobotBoundary boundary;
54 428 gtress
        JButton btnCommand_MoveTo;
55 429 gtress
        JButton btnCommand_MoveAll;
56 320 gtress
        JButton btnCommand_StopTask;
57
        JButton btnCommand_ResumeTask;
58
        JButton btnCommand_ChargeNow;
59
        JButton btnCommand_StopCharging;
60 527 emarinel
61 32 gtress
        // Task Manager
62
        JPanel panelTaskManager;
63
        JScrollPane spTaskManager;
64
        JPanel panelTaskManagerControls;
65
        JPanel panelTaskManagerControlsPriority;
66
        DefaultListModel taskListModel;
67
        JList taskList;
68
        JButton btnAddTask;
69
        JButton btnRemoveTask;
70
        JButton btnMoveTaskUp;
71
        JButton btnMoveTaskDown;
72 320 gtress
        JButton btnUpdateTasks;
73
        TaskAddWindow taskAddWindow;
74 527 emarinel
75 585 gtress
        // Webcam
76 320 gtress
        WebcamPanel panelWebcam;
77 32 gtress
        GraphicsConfiguration gc;
78 320 gtress
        JTabbedPane tabPaneMain;
79 585 gtress
        WebcamLoader webcamLoader;
80
81
        // Robots
82 531 emarinel
        volatile int selectedBot;         //the user has selected this bot graphically
83 671 gtress
        volatile RobotList robotIcons;         //contains boundary shapes around bots for click detection
84 320 gtress
        volatile int[] xbeeID;
85 527 emarinel
86 501 gtress
        Colonet self = this;
87 549 gtress
        volatile ColonetServerInterface csi;
88 32 gtress
89
        public void init () {
90 701 gtress
                // Set the default look and feel
91
                String laf = ColonetConstants.LOOK_AND_FEEL;
92 531 emarinel
                try {
93
                        UIManager.setLookAndFeel(laf);
94
                } catch (UnsupportedLookAndFeelException exc) {
95
                        System.err.println ("Warning: UnsupportedLookAndFeel: " + laf);
96
                } catch (Exception exc) {
97
                        System.err.println ("Error loading " + laf + ": " + exc);
98
                }
99
100 32 gtress
                // We should invoke and wait to avoid browser display difficulties
101
                Runnable r = new Runnable() {
102
                        public void run() {
103
                                createAndShowGUI();
104
                        }
105
                };
106 531 emarinel
107 32 gtress
                try {
108
                        SwingUtilities.invokeAndWait(r);
109
                } catch (InterruptedException e) {
110
                        //Not really sure why we would be in this situation
111 320 gtress
                        System.out.println("InterruptedException in init: " + e);
112 32 gtress
                } catch (java.lang.reflect.InvocationTargetException e) {
113 320 gtress
                        //This could happen for various reasons if there is a problem in createAndShowGUI
114
                        e.printStackTrace();
115 32 gtress
                }
116
        }
117 527 emarinel
118 32 gtress
        public void destroy () {
119 633 emarinel
                if (csi != null) {
120
                        csi.disconnect();
121 570 gtress
                }
122 32 gtress
        }
123
124
        private synchronized void createAndShowGUI () {
125 585 gtress
                // Webcam
126 527 emarinel
                gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
127 320 gtress
                panelWebcam = new WebcamPanel();
128
                tabPaneMain = new JTabbedPane();
129 739 gtress
                tabPaneMain.setFont(new Font("arial", Font.PLAIN, 14));
130 320 gtress
                tabPaneMain.add(panelWebcam, "Webcam");
131 527 emarinel
132 585 gtress
                // Robots
133 428 gtress
                selectedBot = -1;
134 671 gtress
                robotIcons = new RobotList();
135 527 emarinel
136 32 gtress
                // Connection area
137
                txtInfo = new JTextArea();
138
                txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
139 470 gtress
                txtHost = new JTextField(this.getDocumentBase().getHost());
140 32 gtress
                txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
141
                txtPort = new JTextField("10123");
142
                txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
143
                btnConnect = new JButton("Connect");
144 555 emarinel
                btnConnect.setFont(new Font("arial", Font.BOLD, 16));
145 414 gtress
                btnGetXBeeIDs = new JButton("Get XBee IDs");
146 389 gtress
                getRootPane().setDefaultButton(btnConnect);
147 32 gtress
                lblConnectionStatus = new JLabel("Status: Offline");
148
                panelConnect = new JPanel();
149
                panelConnect.setLayout(new GridLayout(6,1));
150
                panelConnect.add(lblConnectionStatus);
151
                panelConnect.add(txtHost);
152
                panelConnect.add(txtPort);
153
                panelConnect.add(btnConnect);
154
                panelServerInterface = new JPanel();
155
                panelServerInterface.setLayout(new GridLayout(2,1));
156
                panelServerInterface.add(panelConnect);
157 585 gtress
                panelServerInterface.add(txtInfo);
158 527 emarinel
159 32 gtress
                // Robot direction panel
160
                panelRobotDirection = new JPanel();
161 320 gtress
                panelRobotDirectionButtons = new JPanel();
162 555 emarinel
163 739 gtress
                Font f = new Font(null, Font.PLAIN, 16);
164 555 emarinel
                btnF = new JButton("\u2191");
165
                btnF.setFont(f);
166
                btnB = new JButton("\u2193");
167
                btnB.setFont(f);
168
                btnL = new JButton("\u2190");
169
                btnL.setFont(f);
170
                btnR = new JButton("\u2192");
171
                btnR.setFont(f);
172
                btnActivate = new JButton("\u25A0");
173 710 gtress
                btnActivate.setFont(new Font(null, Font.BOLD, 24));
174 555 emarinel
175 320 gtress
                panelRobotDirectionButtons.setLayout(new GridLayout(1,5));
176
                panelRobotDirectionButtons.add(btnActivate);
177
                panelRobotDirectionButtons.add(btnF);
178
                panelRobotDirectionButtons.add(btnB);
179
                panelRobotDirectionButtons.add(btnL);
180
                panelRobotDirectionButtons.add(btnR);
181 527 emarinel
182 701 gtress
                imageVectorControl = gc.createCompatibleImage(ColonetConstants.VECTOR_CONTROLLER_WIDTH, ColonetConstants.VECTOR_CONTROLLER_HEIGHT);
183 638 gtress
                vectorController = new VectorController(imageVectorControl, self);
184 320 gtress
                panelRobotDirection.setLayout(new BorderLayout());
185 585 gtress
                panelRobotDirection.add(vectorController, BorderLayout.CENTER);
186 320 gtress
                panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
187 527 emarinel
188 32 gtress
                // Robot Control and Commands
189
                panelRobotCommands = new JPanel();
190 739 gtress
                panelRobotCommands.setLayout(new GridLayout(6,2));
191 333 gtress
                // Battery subset
192 514 gtress
                batteryIcon = new BatteryIcon(0);
193 333 gtress
                lblBattery = new JLabel(batteryIcon);
194 428 gtress
                lblSelected = new JLabel("None");
195 664 gtress
                // Management subset
196 702 gtress
                setStation = false;
197 428 gtress
                setWaypoint = false;
198 429 gtress
                setWaypointID = -1;
199 427 gtress
                btnAssignID = new JButton("Assign ID");
200 739 gtress
                btnLocateStation = new JButton("Identify Station");
201
                boundary = new RobotBoundary();
202
                btnSetBounds = new JButton("Set Boundary");
203
                btnClearBounds = new JButton("Clear Boundary");
204
                // Control subset
205 428 gtress
                btnCommand_MoveTo = new JButton("Move to ...");
206 429 gtress
                btnCommand_MoveAll = new JButton("Move all ...");
207 320 gtress
                btnCommand_StopTask = new JButton("Stop Current Task");
208
                btnCommand_ResumeTask = new JButton("Resume Current Task");
209
                btnCommand_ChargeNow = new JButton("Recharge Now");
210
                btnCommand_StopCharging = new JButton("Stop Recharging");
211
                panelRobotCommands.add(new JLabel("Battery Level: "));
212
                panelRobotCommands.add(lblBattery);
213 428 gtress
                panelRobotCommands.add(new JLabel("Selected Icon: "));
214
                panelRobotCommands.add(lblSelected);
215 427 gtress
                panelRobotCommands.add(btnAssignID);
216 702 gtress
    panelRobotCommands.add(btnLocateStation);
217 739 gtress
                panelRobotCommands.add(btnCommand_ChargeNow);
218
                panelRobotCommands.add(btnCommand_StopCharging);
219 664 gtress
                panelRobotCommands.add(btnSetBounds);
220 682 gtress
    panelRobotCommands.add(btnClearBounds);
221 428 gtress
                panelRobotCommands.add(btnCommand_MoveTo);
222 429 gtress
                panelRobotCommands.add(btnCommand_MoveAll);
223 427 gtress
                //panelRobotCommands.add(btnCommand_StopTask);
224
                //panelRobotCommands.add(btnCommand_ResumeTask);
225 32 gtress
                panelRobotControl = new JPanel();
226
                panelRobotControl.setLayout(new GridLayout(2,1));
227
                panelRobotControl.add(panelRobotDirection);
228
                panelRobotControl.add(panelRobotCommands);
229 527 emarinel
230 32 gtress
                // Task Manager
231
                panelTaskManager = new JPanel();
232
                panelTaskManager.setLayout(new BorderLayout());
233
                taskListModel = new DefaultListModel();
234
                taskList = new JList(taskListModel);
235
                taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
236
                taskList.setSelectedIndex(0);
237
                spTaskManager = new JScrollPane(taskList);
238
                panelTaskManagerControls = new JPanel();
239 320 gtress
                panelTaskManagerControls.setLayout(new GridLayout(1,4));
240 32 gtress
                panelTaskManagerControlsPriority = new JPanel();
241
                panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
242
                btnAddTask = new JButton("Add...");
243
                btnRemoveTask = new JButton("Remove");
244
                btnMoveTaskUp = new JButton("^");
245
                btnMoveTaskDown = new JButton("v");
246 320 gtress
                btnUpdateTasks = new JButton("Update");
247 32 gtress
                panelTaskManagerControlsPriority.add(btnMoveTaskUp);
248
                panelTaskManagerControlsPriority.add(btnMoveTaskDown);
249
                panelTaskManagerControls.add(btnAddTask);
250
                panelTaskManagerControls.add(btnRemoveTask);
251 320 gtress
                panelTaskManagerControls.add(btnUpdateTasks);
252 32 gtress
                panelTaskManagerControls.add(panelTaskManagerControlsPriority);
253
                panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
254
                panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
255
                panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
256 320 gtress
                taskAddWindow = new TaskAddWindow();
257 527 emarinel
258 32 gtress
                // Main control mechanism
259
                tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
260 739 gtress
                tabPaneControl.setFont(new Font("arial", Font.PLAIN, 14));
261 701 gtress
                tabPaneControl.setPreferredSize(new Dimension(ColonetConstants.VECTOR_CONTROLLER_WIDTH, 0));
262 32 gtress
                tabPaneControl.addTab("Connection", panelServerInterface);
263
                tabPaneControl.addTab("Robots", panelRobotControl);
264 514 gtress
                //tabPaneControl.addTab("Tasks", panelTaskManager);
265 527 emarinel
266 320 gtress
                // Put all elements in the ContentPane
267 32 gtress
                this.getContentPane().setLayout(new BorderLayout());
268 320 gtress
                this.getContentPane().add(tabPaneMain, BorderLayout.CENTER);
269 702 gtress
                this.getContentPane().add(tabPaneControl, BorderLayout.EAST);
270 32 gtress
                this.setVisible(true);
271 559 gtress
272
                // Disable components before connecting
273
                btnConnect.setText("Connect");
274 682 gtress
    lblConnectionStatus.setText("Status: Disconnected");
275 702 gtress
    setComponentsEnabled(false);
276 527 emarinel
277 320 gtress
                /* Add all listeners here */
278
                // Task Management
279 32 gtress
                btnAddTask.addActionListener(this);
280
                btnRemoveTask.addActionListener(this);
281
                btnMoveTaskUp.addActionListener(this);
282
                btnMoveTaskDown.addActionListener(this);
283 320 gtress
                btnUpdateTasks.addActionListener(this);
284
                // Robot Control
285
                btnF.addActionListener(this);
286
                btnB.addActionListener(this);
287
                btnL.addActionListener(this);
288
                btnR.addActionListener(this);
289
                btnF.addKeyListener(this);
290
                btnB.addKeyListener(this);
291
                btnL.addKeyListener(this);
292
                btnR.addKeyListener(this);
293
                btnActivate.addActionListener(this);
294
                btnActivate.addKeyListener(this);
295 428 gtress
                btnCommand_MoveTo.addActionListener(this);
296 429 gtress
                btnCommand_MoveAll.addActionListener(this);
297 320 gtress
                btnCommand_StopTask.addActionListener(this);
298
                btnCommand_ResumeTask.addActionListener(this);
299
                btnCommand_ChargeNow.addActionListener(this);
300
                btnCommand_StopCharging.addActionListener(this);
301
                // Other
302 32 gtress
                btnConnect.addActionListener(this);
303 414 gtress
                btnGetXBeeIDs.addActionListener(this);
304 427 gtress
                btnAssignID.addActionListener(this);
305 702 gtress
                btnLocateStation.addActionListener(this);
306 682 gtress
    btnSetBounds.addActionListener(this);
307
    btnClearBounds.addActionListener(this);
308 527 emarinel
                panelWebcam.addMouseListener(this);
309 670 gtress
                panelWebcam.addMouseMotionListener(this);
310 32 gtress
        }
311 638 gtress
312
        public ColonetServerInterface getCSI () {
313 739 gtress
          return csi;
314 638 gtress
        }
315 527 emarinel
316 428 gtress
        public void paint (Graphics g) {
317 531 emarinel
                super.paint(g);
318 428 gtress
        }
319 527 emarinel
320 429 gtress
        public void update (Graphics g) {
321 531 emarinel
                paint(g);
322 429 gtress
        }
323 527 emarinel
324
        /**
325 585 gtress
        * Gets the JTextArea used for displaying debugging information.
326 333 gtress
        *
327 585 gtress
        * @return the JTextArea where debugging information can be displayed.
328 527 emarinel
        */
329 585 gtress
        public JTextArea getInfoPanel () {
330
                return txtInfo;
331 320 gtress
        }
332 527 emarinel
333
        /**
334 333 gtress
        * Parses a String containing BOM matrix information.
335 531 emarinel
        * The ColonetServerInterface receives lines of the BOM matrix.        (For encoding
336
        * information, see the ColonetServerInterface documentation.)         The entire matrix is passed
337 527 emarinel
        * to the client when requested. This method takes a string of the form
338 333 gtress
        * "[command code] [command code] [number of robots] [data0] [data1] ..."
339 527 emarinel
        * with tokens separated by spaces and containing no brackets.
340 333 gtress
        * The [command code]s are predefined values identifying this String as a BOM data
341 527 emarinel
        * String, [number of robots] is an integer, and the values that follow are
342 531 emarinel
        * the sensor readings of the robots in order, starting with robot 0.        Only [number of robots]^2
343
        * data entries will be read.        The matrix values are saved locally until the next String is parsed.
344 333 gtress
        *
345 527 emarinel
        *
346 333 gtress
        * @param line the String containing BOM matrix information.
347
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots]^2 data entries in the String
348
        */
349 320 gtress
        public void parseMatrix (String line) {
350 585 gtress
                txtInfo.setText("");
351 320 gtress
                String [] str = line.split(" ");
352
                int num = Integer.parseInt(str[2]);
353
                for (int i = 0; i < num; i++) {
354
                        for (int j = 0; j < num; j++) {
355
                                String next = str[3 + i*num + j];
356 531 emarinel
                                if (next.equals("-1")) {
357 585 gtress
                                        txtInfo.append("-");
358 531 emarinel
                                } else {
359 585 gtress
                                        txtInfo.append(next);
360 531 emarinel
                                }
361
362
                                if (j < num - 1) {
363 585 gtress
                                        txtInfo.append(" ");
364 531 emarinel
                                }
365 320 gtress
                        }
366 531 emarinel
367
                        if (i < num - 1) {
368 585 gtress
                                txtInfo.append("\n");
369 531 emarinel
                        }
370 136 gtress
                }
371 510 gtress
                repaint();
372 136 gtress
        }
373 527 emarinel
374 420 gtress
        public void connect () {
375 682 gtress
    if (csi != null)
376
      return;
377
    csi = new ColonetServerInterface(self);
378
    csi.connect(txtHost.getText(), txtPort.getText());
379
    if (!csi.isReady()) {
380
      csi = null;
381
      return;
382
    }
383
    webcamLoader = new WebcamLoader(self);
384
    dataUpdater = new DataUpdater();
385
    dataUpdater.start();
386
    webcamLoader.start();
387
    Runnable r = new Runnable() {
388
      public void run () {
389
        btnConnect.setText("Disconnect");
390
        lblConnectionStatus.setText("Status: Connected");
391 702 gtress
        setComponentsEnabled(true);
392 682 gtress
      }
393
    };
394
    SwingUtilities.invokeLater(r);
395 420 gtress
        }
396 527 emarinel
397 420 gtress
        public void disconnect () {
398 702 gtress
    try {
399
        dataUpdater.interrupt();
400
    } catch (Exception e) {
401
    }
402
    csi = null;
403 559 gtress
                Runnable r = new Runnable() {
404 702 gtress
      public void run () {
405
                    btnConnect.setText("Connect");
406
              lblConnectionStatus.setText("Status: Disconnected");
407
        setComponentsEnabled(false);
408 708 gtress
        boundary = null;
409
        station = null;
410 702 gtress
            }
411 559 gtress
                };
412
                SwingUtilities.invokeLater(r);
413 420 gtress
        }
414 702 gtress
415
        private void setComponentsEnabled (boolean enabled) {
416
    btnF.setEnabled(enabled);
417
    btnB.setEnabled(enabled);
418
    btnL.setEnabled(enabled);
419
    btnR.setEnabled(enabled);
420
    btnActivate.setEnabled(enabled);
421
    btnAssignID.setEnabled(enabled);
422
    btnLocateStation.setEnabled(enabled);
423
    btnSetBounds.setEnabled(enabled);
424
    btnClearBounds.setEnabled(enabled);
425 739 gtress
    btnCommand_ChargeNow.setEnabled(enabled);
426
    btnCommand_StopCharging.setEnabled(enabled);
427 702 gtress
    btnCommand_MoveTo.setEnabled(enabled);
428
    btnCommand_MoveAll.setEnabled(enabled);
429
        }
430 527 emarinel
431 333 gtress
        /**
432
        * Parses a String containing a task queue update.
433
        * Format is currently not specified.
434
        * This method currently does nothing.
435
        *
436
        * @param line the String containing task queue update information.
437
        */
438 320 gtress
        public void parseQueue (String line) {
439 585 gtress
440 320 gtress
        }
441 527 emarinel
442 333 gtress
        /**
443
        * Parses a String containing XBee ID values.
444 531 emarinel
        * The ColonetServerInterface receives Strings of XBee information.        (For encoding
445
        * information, see the ColonetServerInterface documentation.)         This method takes
446 333 gtress
        * a string of the form "[command code] [command code] [number of robots] [id0] [id1] ..."
447 527 emarinel
        * with tokens separated by spaces and containing no brackets.
448 333 gtress
        * The [command code]s are predefined values identifying this String as an XBee
449 527 emarinel
        * ID String, [number of robots] is an integer, and the values that follow are
450 531 emarinel
        * the IDs of the robots in order, starting with robot 0.        Only [number of robots]
451
        * will be read.         The ID values are saved locally until the next String is parsed.
452 333 gtress
        * The purpose of having this list is to ensure that robots are properly identified for control purposes.
453 527 emarinel
        * This keeps robot identification consistent between sessions and prevents arbitrary assignment.
454 333 gtress
        *
455
        * @param line the String containing XBee ID information.
456
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots] IDs in the String
457
        * @see ColonetServerInterface#sendXBeeIDRequest()
458
        */
459 320 gtress
        public void parseXBeeIDs (String line) {
460
                String [] str = line.split(" ");
461
                int num = Integer.parseInt(str[2]);
462
                xbeeID = new int[num];
463 531 emarinel
                for (int i = 0; i < num; i++) {
464 320 gtress
                        xbeeID[i] = Integer.parseInt(str[i+3]);
465 531 emarinel
                }
466 320 gtress
        }
467 527 emarinel
468 333 gtress
        /**
469
        * Parses a String containing battery information.
470 531 emarinel
        * The ColonetServerInterface receives Strings of battery information.         (For encoding
471
        * information, see the ColonetServerInterface documentation.)         This method takes
472 333 gtress
        * a string of the form "[command code] [command code] [robot ID] [value]"
473 527 emarinel
        * with tokens separated by spaces and containing no brackets.
474 333 gtress
        * The [command code]s are predefined values identifying this String as a battery
475
        * information String, [robot ID] is an integer, and [value] is a battery measurement.
476
        * This updates the batery information for a single robot.
477
        *
478 527 emarinel
        *
479 333 gtress
        * @param line the String containing battery information.
480
        * @see ColonetServerInterface#sendBatteryRequest(int)
481
        */
482 320 gtress
        public void parseBattery (String line) {
483 739 gtress
          int botNum, level;
484
          try {
485 625 gtress
                    String [] str = line.split(" ");
486
                    botNum = Integer.parseInt(str[2]);
487
                    level = Integer.parseInt(str[3]);
488
                } catch (NumberFormatException e) {
489
                    System.out.println("Could not parse battery update");
490
                    return;
491
                }
492 531 emarinel
493 561 gtress
                RobotIcon r = robotIcons.get(botNum);
494
                if (r != null) {
495 625 gtress
                    r.battery = batteryIcon.convert(level);  // set contextual battery meter
496 638 gtress
                    batteryIcon.setLevel(level);             // set solo battery meter
497 425 gtress
                }
498 638 gtress
                super.repaint();
499 320 gtress
        }
500 527 emarinel
501 459 gtress
        /**
502 527 emarinel
        * Parses a String containing visual robot position information along with
503 459 gtress
        * canonical ID assignments.
504
        */
505
        public void parsePositions (String line) {
506
                String [] str = line.split(" ");
507 671 gtress
                RobotList newList = new RobotList();
508 527 emarinel
509 459 gtress
                for (int i = 2; i < str.length; i+=3) {
510
                        int id = Integer.parseInt(str[i]);
511
                        int x = Integer.parseInt(str[i+1]);
512
                        int y = Integer.parseInt(str[i+2]);
513 470 gtress
                        RobotIcon newIcon = new RobotIcon(id, x, y);
514 646 gtress
                        // Save previous robot information
515
                        RobotIcon oldIcon = robotIcons.get(id);
516
                        if (oldIcon != null) {
517
                            newIcon.battery = oldIcon.battery;
518
                                newIcon.destx = oldIcon.destx;
519
                                newIcon.desty = oldIcon.desty;
520
                        }
521 531 emarinel
                        if (newIcon.id >= 0) {
522
                                newIcon.color = Color.GREEN;
523
                        }
524 671 gtress
                        newList.put(id, newIcon);
525 459 gtress
                }
526 671 gtress
                robotIcons = newList;
527 510 gtress
                repaint();
528 459 gtress
        }
529 682 gtress
530
  /**
531
    * Parses a message that indicates a robot has reached its destination
532
   */
533
  public void parseMoveUpdate (String line) {
534
    System.out.println("Got move update: " + line);
535
    String [] str = line.split(" ");
536
    int id = Integer.parseInt(str[1]);
537
    robotIcons.cancelMoveTo(id);
538
  }
539 671 gtress
540
        /**
541
        * Set the ID of the selected robot
542
        */
543
        public void setSelectedBot (int id) {
544
            this.selectedBot = id;
545
        }
546
547
        /**
548
        * Returns the ID of the selected robot
549
        */
550
        public int getSelectedBot () {
551
            return this.selectedBot;
552
        }
553 527 emarinel
554 32 gtress
        //
555 320 gtress
        // MouseListener methods
556 32 gtress
        //
557 501 gtress
        public void mousePressed(MouseEvent e) {
558 531 emarinel
                //Start a new Thread to handle the MouseEvent
559
                (new MouseHandler(e)).start();
560 501 gtress
        }
561 320 gtress
        public void mouseExited(MouseEvent e) {
562
        }
563
        public void mouseEntered(MouseEvent e) {
564
        }
565
        public void mouseReleased(MouseEvent e) {
566 664 gtress
    (new MouseHandler(e)).start();
567 320 gtress
        }
568
        public void mouseClicked(MouseEvent e) {
569
        }
570
        public void mouseDragged(MouseEvent e) {
571 664 gtress
    (new MouseHandler(e)).start();
572 320 gtress
        }
573
        public void mouseMoved(MouseEvent e) {
574
        }
575 527 emarinel
576 320 gtress
        //
577
        // KeyListener methods
578
        //
579
        public void keyPressed (KeyEvent e) {
580 503 gtress
                //Start a new Thread to handle the KeyEvent
581 531 emarinel
                (new KeyHandler(e)).start();
582 579 gtress
                repaint();
583 181 gtress
        }
584 320 gtress
        public void keyReleased (KeyEvent e) {
585
        }
586
        public void keyTyped (KeyEvent e) {
587
        }
588 527 emarinel
589 320 gtress
        //
590
        // ActionListener method
591
        //
592
        public void actionPerformed (ActionEvent e) {
593 501 gtress
                // Start a new Thread to handle the ActionEvent
594 510 gtress
                (new ActionHandler(e)).start();
595 579 gtress
                repaint();
596 501 gtress
        }
597 527 emarinel
598 501 gtress
        class MouseHandler extends Thread {
599 531 emarinel
                MouseEvent e;
600 527 emarinel
601 531 emarinel
                public MouseHandler (MouseEvent event) {
602
                        super("MouseHandler");
603
                        this.e = event;
604
                }
605 527 emarinel
606 531 emarinel
                public void run () {
607
                        Point pt = panelWebcam.convertClick(e);
608 527 emarinel
609 531 emarinel
                        // If we are selecting a waypoint (destination) for a specific bot
610 664 gtress
                        if (setWaypoint && setWaypointID >= 0 && e.getID() == MouseEvent.MOUSE_PRESSED) {
611 701 gtress
                          // If the user clicks outside the boundary, do nothing
612
                          if (boundary.isActive() && !boundary.contains(e.getX(), e.getY())) {
613
                            return;
614
                          }
615 531 emarinel
                                setWaypoint = false;
616
                                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
617
                                if (selectedBot < 0) {
618
                                        return;
619
                                }
620 556 gtress
621
                                RobotIcon r = robotIcons.get(selectedBot);
622
                                if (r != null) {
623
                                        r.destx = pt.x;
624
                                        r.desty = pt.y;
625
                                        if (csi != null) {
626
                                                csi.sendAbsoluteMove(r.id, r.destx, r.desty);
627 545 gtress
                                        }
628 539 emarinel
                                }
629 531 emarinel
                                return;
630
                        }
631 702 gtress
632
                        // If we are ordering a charge (right-click on a station)
633
                        if ((e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3)
634
          && e.getID() == MouseEvent.MOUSE_PRESSED
635
          && station != null && station.contains(e.getX(), e.getY())) {
636 739 gtress
        if (selectedBot >= 0)
637
          csi.sendRecharge(selectedBot);
638 702 gtress
                                return;
639
                        }
640 527 emarinel
641 531 emarinel
                        // Right-click also means we are moving a robot
642 664 gtress
                        if ((e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3)
643
          && e.getID() == MouseEvent.MOUSE_PRESSED) {
644 701 gtress
        // If the user clicks outside the boundary, do nothing
645
                          if (boundary.isActive() && !boundary.contains(e.getX(), e.getY())) {
646
                            return;
647
                          }
648 531 emarinel
                                if (selectedBot < 0) {
649
                                        return;
650
                                }
651 527 emarinel
652 556 gtress
                                RobotIcon r = robotIcons.get(selectedBot);
653
                                if (r != null) {
654
                                        r.destx = pt.x;
655
                                        r.desty = pt.y;
656
                                        if (csi != null) {
657
                                                csi.sendAbsoluteMove(r.id, r.destx, r.desty);
658 545 gtress
                                        }
659 531 emarinel
                                }
660
                                return;
661
                        }
662 527 emarinel
663 531 emarinel
                        // If we are setting all waypoints
664 671 gtress
                        if (setWaypoint && e.getID() == MouseEvent.MOUSE_PRESSED) {
665 701 gtress
                          // If the user clicks outside the boundary, do nothing
666
                          if (boundary.isActive() && !boundary.contains(e.getX(), e.getY())) {
667
                            return;
668
                          }
669 531 emarinel
                                setWaypoint = false;
670
                                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
671 596 gtress
                                for (Map.Entry<Integer,RobotIcon> entry : robotIcons.entrySet()) {
672
                                        RobotIcon r = entry.getValue();
673 531 emarinel
                                        r.destx = pt.x;
674
                                        r.desty = pt.y;
675
                                }
676
                                return;
677
                        }
678 664 gtress
679
      // If we are drawing a boundary rectangle
680 702 gtress
      if (boundary != null && boundary.isSetting()) {
681 664 gtress
        // Begin
682
        if (e.getID() == MouseEvent.MOUSE_PRESSED) {
683 670 gtress
          boundary.panel_p1 = new Point(e.getX(), e.getY());
684
          boundary.panel_p2 = new Point(e.getX(), e.getY());
685
          boundary.img_p1 = new Point(pt.x, pt.y);
686 664 gtress
        }
687
        // Resize
688
        else if (e.getID() == MouseEvent.MOUSE_DRAGGED){
689 670 gtress
          boundary.panel_p2 = new Point(e.getX(), e.getY());
690 664 gtress
        }
691 670 gtress
        // Finish
692 664 gtress
        else if (e.getID() == MouseEvent.MOUSE_RELEASED){
693 670 gtress
          boundary.panel_p2 = new Point(e.getX(), e.getY());
694
          boundary.img_p2 = new Point(pt.x, pt.y);
695 664 gtress
          panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
696
          boundary.set = false;
697
          boundary.active = true;
698
          boundary.sendToServer();
699
        }
700
        return;
701
      }
702 702 gtress
703
      // If we are locating a charging station
704
      if (setStation) {
705
        setStation = false;
706
        station = new ChargingStation(e.getX(), e.getY());
707
        station.setActive(true);
708
        return;
709
      }
710 527 emarinel
711 531 emarinel
                        // Otherwise, we are selecting a bot, or doing nothing
712 671 gtress
                        RobotIcon r = robotIcons.getBoundingIcon(pt);
713
      if (r != null)
714
        selectedBot = r.id;
715 531 emarinel
                }
716 320 gtress
        }
717 527 emarinel
718 503 gtress
        class KeyHandler extends Thread {
719
                KeyEvent e;
720 527 emarinel
721 503 gtress
                public KeyHandler (KeyEvent event) {
722
                        super("KeyHandler");
723
                        this.e = event;
724
                }
725 527 emarinel
726 503 gtress
                public void run () {
727
                        int code = e.getKeyCode();
728
                        if (code == KeyEvent.VK_UP) {
729
                                vectorController.setMaxForward();
730
                                vectorController.sendToServer();
731
                        } else if (code == KeyEvent.VK_DOWN) {
732
                                vectorController.setMaxReverse();
733
                                vectorController.sendToServer();
734
                        } else if (code == KeyEvent.VK_LEFT) {
735
                                vectorController.setMaxLeft();
736
                                vectorController.sendToServer();
737
                        } else if (code == KeyEvent.VK_RIGHT) {
738
                                vectorController.setMaxRight();
739
                                vectorController.sendToServer();
740
                        } else if (code == KeyEvent.VK_S) {
741
                                vectorController.setZero();
742
                                vectorController.sendToServer();
743
                        }
744
                }
745
        }
746 527 emarinel
747 501 gtress
        class ActionHandler extends Thread {
748 531 emarinel
                ActionEvent e;
749 527 emarinel
750 531 emarinel
                public ActionHandler (ActionEvent event) {
751
                        super("ActionHandler");
752
                        this.e = event;
753
                }
754 527 emarinel
755 531 emarinel
                public void run () {
756
                        Object source = e.getSource();
757 527 emarinel
758 531 emarinel
                        // General Actions
759
                        if (source == btnConnect) {
760
                                if (csi == null) {
761
                                        connect();
762
                                } else {
763
                                        disconnect();
764
                                }
765
                        } else if (source == btnGetXBeeIDs) {
766
                                csi.sendXBeeIDRequest();
767
                        } else if (source == btnAssignID) {
768
                                String message;
769 556 gtress
                                if (selectedBot < 0) {
770 531 emarinel
                                        message = "That robot is unidentified. Please specify its ID.";
771
                                } else {
772 556 gtress
                                        message = "That robot has ID " + selectedBot + ". You may reassign it now.";
773 531 emarinel
                                }
774
                                String result = JOptionPane.showInputDialog(self, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
775
                                if (result == null) {
776
                                        return;
777
                                }
778
                                int newID = -1;
779
                                try {
780
                                        newID = Integer.parseInt(result);
781
                                } catch (Exception ex) {
782
                                        csi.warn("Invalid ID.");
783
                                        return;
784
                                }
785
                                // Assign new ID and update display
786 549 gtress
                                if (csi != null) {
787 556 gtress
                                        csi.sendIDAssignment(selectedBot, newID);
788 549 gtress
                                }
789 556 gtress
                                selectedBot = newID;
790 545 gtress
                                lblSelected.setText("" + newID);
791 702 gtress
                  } else if (source == btnLocateStation) {
792
                    setStation = true;
793 531 emarinel
                        } else if (source == btnF) { // Robot Movement Controls
794
                                vectorController.setMaxForward();
795
                                vectorController.sendToServer();
796 671 gtress
                                robotIcons.cancelMoveTo(selectedBot);
797 531 emarinel
                        } else if (source == btnB) {
798
                                vectorController.setMaxReverse();
799
                                vectorController.sendToServer();
800 671 gtress
                                robotIcons.cancelMoveTo(selectedBot);
801 531 emarinel
                        } else if (source == btnL) {
802
                                vectorController.setMaxLeft();
803
                                vectorController.sendToServer();
804 671 gtress
                                robotIcons.cancelMoveTo(selectedBot);
805 531 emarinel
                        } else if (source == btnR) {
806
                                vectorController.setMaxRight();
807
                                vectorController.sendToServer();
808 671 gtress
                                robotIcons.cancelMoveTo(selectedBot);
809 531 emarinel
                        } else if (source == btnActivate) {
810
                                vectorController.setZero();
811
                                vectorController.sendToServer();
812 671 gtress
                                robotIcons.cancelMoveTo(selectedBot);
813 664 gtress
      } else if (source == btnSetBounds) {
814
        boundary.set = true;
815
        panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
816 682 gtress
      } else if (source == btnClearBounds) {
817
        boundary.active = false;
818
        boundary.set = false;
819
        boundary.img_p1 = new Point(-1, -1);
820
        boundary.img_p2 = new Point(-1, -1);
821
        boundary.panel_p1 = new Point(-1, -1);
822
        boundary.panel_p2 = new Point(-1, -1);
823
        boundary.sendToServer();
824 664 gtress
                        } else if (source == btnCommand_MoveTo) {
825 531 emarinel
                                if (selectedBot < 0) {
826
                                        return;
827
                                }
828
                                panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
829
                                setWaypoint = true;
830
                                setWaypointID = selectedBot;
831
                        } else if (source == btnCommand_MoveAll) {
832
                                panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
833
                                setWaypoint = true;
834
                                setWaypointID = -1;
835
                        } else if (source == btnCommand_StopTask) {
836 527 emarinel
837 531 emarinel
                        } else if (source == btnCommand_ResumeTask) {
838 527 emarinel
839 531 emarinel
                        } else if (source == btnCommand_ChargeNow) {
840 527 emarinel
841 531 emarinel
                        } else if (source == btnCommand_StopCharging) {
842 527 emarinel
843 531 emarinel
                        } else if (source == btnAddTask) { // Queue Management
844
                                taskAddWindow.prompt();
845
                        } else if (source == btnRemoveTask) {
846
                                if (taskList.getSelectedIndex() >= 0) {
847
                                        csi.sendQueueRemove(taskList.getSelectedIndex());
848
                                }
849
                                csi.sendQueueUpdate();
850
                        } else if (source == btnMoveTaskUp) {
851
                                csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
852
                                csi.sendQueueUpdate();
853
                        } else if (source == btnMoveTaskDown) {
854
                                csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
855
                                csi.sendQueueUpdate();
856
                        } else if (source == btnUpdateTasks) {
857
                                csi.sendQueueUpdate();
858
                        }
859 527 emarinel
860 531 emarinel
                }
861 32 gtress
        }
862 527 emarinel
863 32 gtress
        /*
864 531 emarinel
        * DataUpdater thread.
865 635 gtress
        *        The purpose of this thread is to request data from the server at regular intervals.
866
        *        Types of data requested: XBee IDs, robot positions, battery levels.
867 32 gtress
        *
868
        */
869 320 gtress
        class DataUpdater extends Thread {
870 633 emarinel
                final int DATAUPDATER_DELAY = 200;
871 635 gtress
                int count;
872 320 gtress
                public DataUpdater () {
873
                        super("Colonet DataUpdater");
874 635 gtress
                        count = 0;
875 320 gtress
                }
876 527 emarinel
877 625 gtress
                public synchronized void run () {
878 320 gtress
                        while (true) {
879
                                try {
880
                                        if (csi != null && csi.isReady()) {
881 635 gtress
                                                // XBee ID Request
882
                                                if (count % 5 == 0) {
883
                                                        csi.sendXBeeIDRequest();
884 425 gtress
                                                }
885 635 gtress
                                                // Robot Position Request
886
                                                if (count % 1 == 0) {
887
                                                        csi.sendPositionRequest();
888
                                                }
889
                                                // Battery Request
890
                                                if (count % 30 == 0) {
891
                                                        for (Map.Entry<Integer,RobotIcon> entry : robotIcons.entrySet()) {
892
                                                                RobotIcon r = entry.getValue();
893
                                                                        int id = r.id;
894
                                                                                if (id >= 0) {
895
                                                                                csi.sendBatteryRequest(id);
896
                                                                        }
897
                                                        }
898
                                                }
899 320 gtress
                                        }
900 635 gtress
                                        count++;
901
                                        if (count > 1000)
902
                                                count = 0;
903 320 gtress
                                        Thread.sleep(DATAUPDATER_DELAY);
904
                                } catch (InterruptedException e) {
905
                                        return;
906 625 gtress
                                } catch (NullPointerException e) {
907
                                    // This probably indicates that an object was destroyed by shutdown.
908
                                    return;
909 527 emarinel
                                }
910 320 gtress
                        }
911
                }
912
        }
913 527 emarinel
914
915 320 gtress
        /*
916 531 emarinel
        * WebcamPanel class
917 701 gtress
        * Contains the webcam image
918 320 gtress
        */
919
        class WebcamPanel extends JPanel {
920 701 gtress
921 320 gtress
                volatile BufferedImage img;
922 511 gtress
                BufferedImage buffer;
923 527 emarinel
924 320 gtress
                public WebcamPanel () {
925 517 gtress
                        super(true);
926 320 gtress
                }
927 527 emarinel
928 506 gtress
                public synchronized void setImage (BufferedImage newimg) {
929 320 gtress
                        if (img != null) {
930
                                img.flush();
931 273 gtress
                        }
932 429 gtress
                        System.gc();
933 320 gtress
                        img = newimg;
934 510 gtress
                        repaint();
935 270 gtress
                }
936 527 emarinel
937 506 gtress
                public synchronized void paint (Graphics g) {
938 533 emarinel
                        if (img == null) {
939 427 gtress
                                return;
940 533 emarinel
                        }
941 527 emarinel
942 511 gtress
                        // Calculate scaling
943 701 gtress
                        int maxWidth = getWidth() - 2*ColonetConstants.WEBCAM_BORDER;
944
                        int maxHeight = getHeight() - 2*ColonetConstants.WEBCAM_BORDER;
945 470 gtress
                        double widthRatio = 1.0 * maxWidth / img.getWidth();
946
                        double heightRatio = 1.0 * maxHeight / img.getHeight();
947 504 gtress
                        double scale = 0;
948 470 gtress
                        int newWidth = 0;
949
                        int newHeight = 0;
950
                        int x = 0;
951
                        int y = 0;
952 527 emarinel
953 531 emarinel
                        if (widthRatio > heightRatio) {         //height is the limiting factor
954 533 emarinel
                                scale = heightRatio;
955
                                newHeight = maxHeight;
956
                                newWidth = (int) (img.getWidth() * scale);
957 701 gtress
                                y = ColonetConstants.WEBCAM_BORDER;
958
                                x = (maxWidth - newWidth) / 2 + ColonetConstants.WEBCAM_BORDER;
959 531 emarinel
                        } else {        //width is the limiting factor
960 533 emarinel
                                scale = widthRatio;
961
                                newWidth = maxWidth;
962
                                newHeight = (int) (img.getHeight() * scale);
963 701 gtress
                                x = ColonetConstants.WEBCAM_BORDER;
964
                                y = (maxHeight - newHeight) / 2 + ColonetConstants.WEBCAM_BORDER;
965 427 gtress
                        }
966 527 emarinel
967 664 gtress
                        // Draw image onto the buffer
968 512 gtress
                        buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
969 511 gtress
                        Graphics2D bufferedGraphics = (Graphics2D)buffer.getGraphics();
970
                        bufferedGraphics.setColor(Color.GRAY);
971
                        bufferedGraphics.fillRect(0, 0, this.getWidth(), this.getHeight());
972 470 gtress
                        Image imgScaled = img.getScaledInstance(newWidth, newHeight, Image.SCALE_FAST);
973 511 gtress
                        bufferedGraphics.drawImage(imgScaled, x, y, this);
974 527 emarinel
975 701 gtress
      // Draw boundary
976 758 gtress
      if (boundary != null && (boundary.isSetting() || boundary.isActive())) {
977 701 gtress
         bufferedGraphics.setColor(Color.BLUE);
978
         int width = boundary.panel_p2.x - boundary.panel_p1.x;
979
         int height = boundary.panel_p2.y - boundary.panel_p1.y;
980
         bufferedGraphics.drawRect(boundary.panel_p1.x, boundary.panel_p1.y, width, height);
981
      }
982 702 gtress
983
      // Draw charging station
984
      if (station != null && station.isActive()) {
985
        bufferedGraphics.setStroke(new BasicStroke(2));
986
        bufferedGraphics.setColor(Color.ORANGE);
987
        int width = ColonetConstants.STATION_SIZE;
988
        bufferedGraphics.drawRect(station.getX() - width/2, station.getY() - width/2, width, width);
989
      }
990 527 emarinel
991 505 gtress
                        // Draw Identifiers and battery levels
992 515 gtress
                        if (robotIcons != null) {
993 533 emarinel
                                bufferedGraphics.setStroke(new BasicStroke(2));
994 596 gtress
                                for (Map.Entry<Integer,RobotIcon> entry : robotIcons.entrySet()) {
995
                                        RobotIcon r = entry.getValue();
996 533 emarinel
                                        bufferedGraphics.setColor(r.color);
997
                                        // Identifier circle
998
                                        int px = (int) (x + r.x * scale);
999
                                        int py = (int) (y + r.y * scale);
1000 701 gtress
                                        int radius = ColonetConstants.ROBOT_RADIUS;
1001
                                        bufferedGraphics.drawOval(px-radius, py-radius, 2*radius, 2*radius);
1002 671 gtress
                                        // ID, if applicable
1003
                                        if (r.id > 0) {
1004
                                            bufferedGraphics.setFont(new Font("arial", Font.PLAIN, 36));
1005
                                            bufferedGraphics.drawString("" + r.id, px-10, py+10);
1006
                                        }
1007 533 emarinel
                                        // Battery
1008 561 gtress
                                        if (r.battery >= 0) {
1009 701 gtress
                                            int pixels = r.battery * ColonetConstants.BATTERY_WIDTH / 100;
1010 625 gtress
                                                if (r.battery > 50)
1011
                                                    bufferedGraphics.setColor(Color.GREEN);
1012
                                                else if (r.battery > 25)
1013
                                                    bufferedGraphics.setColor(Color.YELLOW);
1014
                                                else
1015
                                                    bufferedGraphics.setColor(Color.RED);
1016 701 gtress
                                                bufferedGraphics.fillRect(px+20, py+20, pixels, ColonetConstants.BATTERY_HEIGHT);
1017 535 emarinel
                                                bufferedGraphics.setColor(Color.BLACK);
1018 701 gtress
                                                bufferedGraphics.drawRect(px+20, py+20, ColonetConstants.BATTERY_WIDTH, ColonetConstants.BATTERY_HEIGHT);
1019 561 gtress
                                        }
1020 533 emarinel
                                        // If the robot has a destination, draw the vector
1021
                                        if (r.destx >= 0) {
1022
                                                bufferedGraphics.drawLine(px, py, (int)(x + r.destx * scale), (int)(y + r.desty * scale));
1023 531 emarinel
                                        }
1024 533 emarinel
                                }
1025 320 gtress
                        }
1026 527 emarinel
1027 428 gtress
                        // Identify currently-selected robot
1028 556 gtress
                        RobotIcon r = robotIcons.get(selectedBot);
1029
                        if (r != null) {
1030
                                int px = (int) (x + r.x * scale);
1031
                                int py = (int) (y + r.y * scale);
1032 701 gtress
                                int radius = ColonetConstants.ROBOT_RADIUS;
1033 556 gtress
                                bufferedGraphics.setColor(Color.BLACK);
1034 701 gtress
                                bufferedGraphics.drawOval(px-radius-6, py-radius-6, 2*radius+12, 2*radius+12);
1035 515 gtress
                        }
1036 527 emarinel
1037 511 gtress
                        //Display buffered content
1038 515 gtress
                        g.drawImage(buffer, 0, 0, this);
1039 270 gtress
                }
1040 527 emarinel
1041 635 gtress
                /**
1042 527 emarinel
                * Convert a click on the webcam panel to a coordinate that is consistent with the
1043 522 gtress
                * original size of the image that the panel contains.
1044
                */
1045
                public Point convertClick (MouseEvent e) {
1046 532 emarinel
                        if (img == null) {
1047
                                return new Point(e.getX(), e.getY());
1048
                        }
1049
1050 522 gtress
                        // Calculate scaling
1051 701 gtress
                        int border = ColonetConstants.WEBCAM_BORDER;
1052 522 gtress
                        int clickx = e.getX();
1053 532 emarinel
                        int clicky = e.getY();
1054 701 gtress
                        int maxWidth = getWidth() - 2*border;
1055
                        int maxHeight = getHeight() - 2*border;
1056 522 gtress
                        double widthRatio = 1.0 * maxWidth / img.getWidth();
1057
                        double heightRatio = 1.0 * maxHeight / img.getHeight();
1058
                        double scale = 0;
1059
                        int newWidth = 0;
1060
                        int newHeight = 0;
1061
                        int px = 0;
1062
                        int py = 0;
1063 527 emarinel
1064 531 emarinel
                        if (widthRatio > heightRatio) {         //height is the limiting factor
1065 532 emarinel
                                scale = heightRatio;
1066
                                newHeight = maxHeight;
1067
                                newWidth = (int) (img.getWidth() * scale);
1068 701 gtress
                                py = clicky - border;
1069
                                px = clickx - border - (maxWidth - newWidth) / 2;
1070 531 emarinel
                        } else {        //width is the limiting factor
1071 532 emarinel
                                scale = widthRatio;
1072
                                newWidth = maxWidth;
1073
                                newHeight = (int) (img.getHeight() * scale);
1074 701 gtress
                                px = clickx - border;
1075
                                py = clicky - border - (maxHeight - newHeight) / 2;
1076 522 gtress
                        }
1077 549 gtress
                        py = (int) (py / scale);
1078
                        px = (int) (px / scale);
1079 532 emarinel
                        return new Point(px, py);
1080 522 gtress
                }
1081 667 gtress
1082 270 gtress
        }
1083 527 emarinel
1084 270 gtress
        /*
1085 531 emarinel
        * WebcamLoader class
1086
        * Handles the loading of the webcam image.
1087 270 gtress
        */
1088 527 emarinel
        class WebcamLoader extends Thread
1089 320 gtress
        {
1090
                URL imagePath;
1091
                MediaTracker mt;
1092
                BufferedImage image;
1093 471 gtress
                Random rand;
1094 527 emarinel
1095 320 gtress
                public WebcamLoader (JApplet applet)
1096
                {
1097
                        super("ColonetWebcamLoader");
1098
                        mt = new MediaTracker(applet);
1099
                        ImageIO.setUseCache(false);
1100 471 gtress
                        rand = new Random();
1101 270 gtress
                }
1102 527 emarinel
1103 470 gtress
                public void run ()
1104 320 gtress
                {
1105 270 gtress
                        while (true) {
1106
                                try {
1107 701 gtress
                                        Thread.sleep(ColonetConstants.WEBCAM_DELAY);
1108 527 emarinel
                                        if (image != null)
1109 320 gtress
                                                image.flush();
1110 429 gtress
                                        System.gc();
1111 471 gtress
                                        try {
1112 701 gtress
                                                imagePath = new URL(ColonetConstants.WEBCAM_PATH + "?rand=" + rand.nextInt(100000));
1113 535 emarinel
                                        } catch (MalformedURLException e) {
1114 701 gtress
                                                System.out.println("Malformed URL: could not form URL from: [" + ColonetConstants.WEBCAM_PATH + "]\n");
1115 535 emarinel
                                        }
1116 320 gtress
                                        image = ImageIO.read(imagePath);
1117
                                        // The MediaTracker waitForID pauses the thread until the image is loaded.
1118
                                        // We don't want to display a half-downloaded image.
1119
                                        mt.addImage(image, 1);
1120 679 gtress
                                        while(!mt.checkID(1))
1121
                                                Thread.sleep(20);
1122 320 gtress
                                        mt.removeImage(image);
1123
                                        // Save
1124
                                        panelWebcam.setImage(image);
1125 270 gtress
                                } catch (InterruptedException e) {
1126
                                        return;
1127 320 gtress
                                } catch (java.security.AccessControlException e) {
1128 389 gtress
                                        csi.warn("Could not load webcam.\n" + e);
1129 320 gtress
                                        return;
1130
                                } catch (IOException e) {
1131 585 gtress
                                        getInfoPanel().append("IOException while trying to load image.");
1132 320 gtress
                                }
1133 270 gtress
                        }
1134
                }
1135 459 gtress
        }
1136 664 gtress
1137
  class RobotBoundary {
1138 667 gtress
    public volatile boolean active, set;
1139 670 gtress
    public volatile Point img_p1, img_p2;
1140
    public volatile Point panel_p1, panel_p2;
1141 664 gtress
1142
    public RobotBoundary () {
1143
      active = false;
1144
      set = false;
1145 670 gtress
      img_p1 = new Point(-1,-1);
1146
      img_p2 = new Point(-1,-1);
1147
      panel_p1 = new Point(-1,-1);
1148
      panel_p2 = new Point(-1,-1);
1149 664 gtress
    }
1150
1151
    public void sendToServer () {
1152 682 gtress
      if (csi != null) {
1153 698 gtress
        if (img_p1.x == -1 || img_p1.y == -1)
1154
          csi.sendBoundaryClear();
1155
        else
1156
          csi.sendBoundary(img_p1.x, img_p1.y, img_p2.x, img_p2.y);
1157 682 gtress
      }
1158 664 gtress
    }
1159 701 gtress
1160
    public boolean isActive () {
1161
      return active;
1162
    }
1163
1164 702 gtress
    public boolean isSetting () {
1165 701 gtress
      return set;
1166
    }
1167
1168
    /**
1169
    *  Returns a Rectangle designating the active area
1170
    *  in the coordinate system of the JPanel.
1171
    *  Returns null if the boundary is not active.
1172
    */
1173 702 gtress
    public Rectangle toRectangle () {
1174 701 gtress
      if (!active)
1175
        return null;
1176
      return new Rectangle (panel_p1.x, panel_p1.y, (panel_p2.x-panel_p1.x), (panel_p2.y-panel_p1.y));
1177
    }
1178
1179
    /**
1180
    *  Determines whether a coordinate in the coordinate
1181
    *  system of the JPanel is contained within the boundary.
1182
    *  If the boundary is not active, then this method
1183
    *  returns false.
1184
    */
1185 702 gtress
    public boolean contains (int px, int py) {
1186
      Rectangle rect = this.toRectangle();
1187 701 gtress
      if (rect == null || !this.active)
1188
        return false;
1189 702 gtress
      return rect.contains(px, py);
1190 701 gtress
    }
1191
1192 664 gtress
  }
1193 702 gtress
1194
  class ChargingStation {
1195
    private int x, y;
1196
    private boolean active;
1197
1198
    public ChargingStation () {
1199
      super();
1200
    }
1201
1202
    public ChargingStation (int x, int y) {
1203
      this();
1204
      setLocation(x, y);
1205
    }
1206
1207
    public int getX () {
1208
      return x;
1209
    }
1210
1211
    public int getY () {
1212
      return y;
1213
    }
1214
1215
    /**
1216
    *  Sets the center of the charging station to
1217
    *  the specified coordinate point.
1218
    */
1219
    public void setLocation (int x, int y) {
1220
      this.x = x;
1221
      this.y = y;
1222
    }
1223
1224
    public boolean isActive () {
1225
      return active;
1226
    }
1227
1228
    public void setActive (boolean status) {
1229
      this.active = status;
1230
    }
1231
1232
    /**
1233
    *  Determines whether a coordinate is contained within
1234
    *  the graphical area of the charging station.
1235
    *  Returns false if the charging station is not active.
1236
    */
1237
    public boolean contains (int px, int py) {
1238
      if (!active)
1239
        return false;
1240
      int width = ColonetConstants.STATION_SIZE;
1241
      Rectangle rect = new Rectangle(this.x - width/2, this.y - width/2, width, width);
1242
      return rect.contains(px, py);
1243
    }
1244
1245
  }
1246 527 emarinel
1247 638 gtress
}