Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (38.6 KB)

1
import javax.swing.*;
2
import javax.swing.event.*;
3
import javax.imageio.*;
4
import java.awt.*;
5
import java.awt.image.*;
6
import java.awt.event.*;
7
import java.net.*;
8
import java.io.*;
9
import java.util.*;
10

    
11
/**
12
* The Colonet Graphical User Interface Applet for use locally and over an internet connection.
13
* @author Gregory Tress
14
*
15
* 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
*/
18
public class Colonet extends JApplet implements ActionListener, MouseInputListener, KeyListener {
19

    
20
        // Connection
21
        JTextField txtHost;
22
        JTextField txtPort;
23
        JButton btnConnect;
24
        JButton btnGetXBeeIDs;
25
        JLabel lblConnectionStatus;
26
        JTextArea txtInfo;
27
        JPanel panelConnect;
28
        JPanel panelServerInterface;
29
        Socket socket;
30
        DataUpdater dataUpdater;
31

    
32
        // Control
33
        JTabbedPane tabPaneControl;
34
        JPanel panelRobotControl;
35
        JPanel panelRobotDirection;
36
        JPanel panelRobotDirectionButtons;
37
        JPanel panelRobotCommands;
38
        JButton btnF, btnB, btnL, btnR, btnActivate;
39
        JLabel lblBattery;
40
        JLabel lblSelected;
41
        BatteryIcon batteryIcon;
42
        JPanel panelBattery;
43
        VectorController vectorController;
44
        BufferedImage imageVectorControl;
45
        JButton btnAssignID;
46
        JButton btnLocateStation;
47
        boolean setStation;
48
        ChargingStation station;
49
        boolean setWaypoint;
50
        int setWaypointID;
51
        JButton btnSetBounds;
52
        JButton btnClearBounds;
53
        RobotBoundary boundary;
54
        JButton btnCommand_MoveTo;
55
        JButton btnCommand_MoveAll;
56
        JButton btnCommand_StopTask;
57
        JButton btnCommand_ResumeTask;
58
        JButton btnCommand_ChargeNow;
59
        JButton btnCommand_StopCharging;
60

    
61
        // 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
        JButton btnUpdateTasks;
73
        TaskAddWindow taskAddWindow;
74

    
75
        // Webcam
76
        WebcamPanel panelWebcam;
77
        GraphicsConfiguration gc;
78
        JTabbedPane tabPaneMain;
79
        WebcamLoader webcamLoader;
80
        
81
        // Robots
82
        volatile int selectedBot;         //the user has selected this bot graphically
83
        volatile RobotList robotIcons;         //contains boundary shapes around bots for click detection
84
        volatile int[] xbeeID;
85

    
86
        Colonet self = this;
87
        volatile ColonetServerInterface csi;
88

    
89
        public void init () {
90
                // Set the default look and feel
91
                String laf = ColonetConstants.LOOK_AND_FEEL;
92
                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
                // We should invoke and wait to avoid browser display difficulties
101
                Runnable r = new Runnable() {
102
                        public void run() {
103
                                createAndShowGUI();
104
                        }
105
                };
106

    
107
                try {
108
                        SwingUtilities.invokeAndWait(r);
109
                } catch (InterruptedException e) {
110
                        //Not really sure why we would be in this situation
111
                        System.out.println("InterruptedException in init: " + e);
112
                } catch (java.lang.reflect.InvocationTargetException e) {
113
                        //This could happen for various reasons if there is a problem in createAndShowGUI
114
                        e.printStackTrace();
115
                }
116
        }
117

    
118
        public void destroy () {
119
                if (csi != null) {
120
                        csi.disconnect();
121
                }
122
        }
123

    
124
        private synchronized void createAndShowGUI () {
125
                // Webcam
126
                gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
127
                panelWebcam = new WebcamPanel();
128
                tabPaneMain = new JTabbedPane();
129
                tabPaneMain.setFont(new Font("arial", Font.PLAIN, 14));
130
                tabPaneMain.add(panelWebcam, "Webcam");
131

    
132
                // Robots
133
                selectedBot = -1;
134
                robotIcons = new RobotList();
135

    
136
                // Connection area
137
                txtInfo = new JTextArea();
138
                txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
139
                txtHost = new JTextField(this.getDocumentBase().getHost());
140
                txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
141
                txtPort = new JTextField("10123");
142
                txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
143
                btnConnect = new JButton("Connect");
144
                btnConnect.setFont(new Font("arial", Font.BOLD, 16));
145
                btnGetXBeeIDs = new JButton("Get XBee IDs");
146
                getRootPane().setDefaultButton(btnConnect);
147
                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
                panelServerInterface.add(txtInfo);
158

    
159
                // Robot direction panel
160
                panelRobotDirection = new JPanel();
161
                panelRobotDirectionButtons = new JPanel();
162

    
163
                Font f = new Font(null, Font.PLAIN, 16);
164
                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
                btnActivate.setFont(new Font(null, Font.BOLD, 24));
174

    
175
                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

    
182
                imageVectorControl = gc.createCompatibleImage(ColonetConstants.VECTOR_CONTROLLER_WIDTH, ColonetConstants.VECTOR_CONTROLLER_HEIGHT);
183
                vectorController = new VectorController(imageVectorControl, self);
184
                panelRobotDirection.setLayout(new BorderLayout());
185
                panelRobotDirection.add(vectorController, BorderLayout.CENTER);
186
                panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
187

    
188
                // Robot Control and Commands
189
                panelRobotCommands = new JPanel();
190
                panelRobotCommands.setLayout(new GridLayout(6,2));
191
                // Battery subset
192
                batteryIcon = new BatteryIcon(0);
193
                lblBattery = new JLabel(batteryIcon);
194
                lblSelected = new JLabel("None");
195
                // Management subset
196
                setStation = false;
197
                setWaypoint = false;
198
                setWaypointID = -1;
199
                btnAssignID = new JButton("Assign ID");
200
                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
                btnCommand_MoveTo = new JButton("Move to ...");
206
                btnCommand_MoveAll = new JButton("Move all ...");
207
                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
                panelRobotCommands.add(new JLabel("Selected Icon: "));
214
                panelRobotCommands.add(lblSelected);
215
                panelRobotCommands.add(btnAssignID);
216
    panelRobotCommands.add(btnLocateStation);
217
                panelRobotCommands.add(btnCommand_ChargeNow);
218
                panelRobotCommands.add(btnCommand_StopCharging);
219
                panelRobotCommands.add(btnSetBounds);
220
    panelRobotCommands.add(btnClearBounds);
221
                panelRobotCommands.add(btnCommand_MoveTo);
222
                panelRobotCommands.add(btnCommand_MoveAll);
223
                //panelRobotCommands.add(btnCommand_StopTask);
224
                //panelRobotCommands.add(btnCommand_ResumeTask);
225
                panelRobotControl = new JPanel();
226
                panelRobotControl.setLayout(new GridLayout(2,1));
227
                panelRobotControl.add(panelRobotDirection);
228
                panelRobotControl.add(panelRobotCommands);
229

    
230
                // 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
                panelTaskManagerControls.setLayout(new GridLayout(1,4));
240
                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
                btnUpdateTasks = new JButton("Update");
247
                panelTaskManagerControlsPriority.add(btnMoveTaskUp);
248
                panelTaskManagerControlsPriority.add(btnMoveTaskDown);
249
                panelTaskManagerControls.add(btnAddTask);
250
                panelTaskManagerControls.add(btnRemoveTask);
251
                panelTaskManagerControls.add(btnUpdateTasks);
252
                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
                taskAddWindow = new TaskAddWindow();
257

    
258
                // Main control mechanism
259
                tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
260
                tabPaneControl.setFont(new Font("arial", Font.PLAIN, 14));
261
                tabPaneControl.setPreferredSize(new Dimension(ColonetConstants.VECTOR_CONTROLLER_WIDTH, 0));
262
                tabPaneControl.addTab("Connection", panelServerInterface);
263
                tabPaneControl.addTab("Robots", panelRobotControl);
264
                //tabPaneControl.addTab("Tasks", panelTaskManager);
265

    
266
                // Put all elements in the ContentPane
267
                this.getContentPane().setLayout(new BorderLayout());
268
                this.getContentPane().add(tabPaneMain, BorderLayout.CENTER);
269
                this.getContentPane().add(tabPaneControl, BorderLayout.EAST);
270
                this.setVisible(true);
271
                
272
                // Disable components before connecting
273
                btnConnect.setText("Connect");
274
    lblConnectionStatus.setText("Status: Disconnected");
275
    setComponentsEnabled(false);
276

    
277
                /* Add all listeners here */
278
                // Task Management
279
                btnAddTask.addActionListener(this);
280
                btnRemoveTask.addActionListener(this);
281
                btnMoveTaskUp.addActionListener(this);
282
                btnMoveTaskDown.addActionListener(this);
283
                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
                btnCommand_MoveTo.addActionListener(this);
296
                btnCommand_MoveAll.addActionListener(this);
297
                btnCommand_StopTask.addActionListener(this);
298
                btnCommand_ResumeTask.addActionListener(this);
299
                btnCommand_ChargeNow.addActionListener(this);
300
                btnCommand_StopCharging.addActionListener(this);
301
                // Other
302
                btnConnect.addActionListener(this);
303
                btnGetXBeeIDs.addActionListener(this);
304
                btnAssignID.addActionListener(this);
305
                btnLocateStation.addActionListener(this);
306
    btnSetBounds.addActionListener(this);
307
    btnClearBounds.addActionListener(this);
308
                panelWebcam.addMouseListener(this);
309
                panelWebcam.addMouseMotionListener(this);
310
        }
311
        
312
        public ColonetServerInterface getCSI () {
313
          return csi;
314
        }
315

    
316
        public void paint (Graphics g) {
317
                super.paint(g);
318
        }
319

    
320
        public void update (Graphics g) {
321
                paint(g);
322
        }
323

    
324
        /**
325
        * Gets the JTextArea used for displaying debugging information. 
326
        *
327
        * @return the JTextArea where debugging information can be displayed.
328
        */
329
        public JTextArea getInfoPanel () {
330
                return txtInfo;
331
        }
332

    
333
        /**
334
        * Parses a String containing BOM matrix information.
335
        * The ColonetServerInterface receives lines of the BOM matrix.        (For encoding
336
        * information, see the ColonetServerInterface documentation.)         The entire matrix is passed
337
        * to the client when requested. This method takes a string of the form
338
        * "[command code] [command code] [number of robots] [data0] [data1] ..."
339
        * with tokens separated by spaces and containing no brackets.
340
        * The [command code]s are predefined values identifying this String as a BOM data
341
        * String, [number of robots] is an integer, and the values that follow are
342
        * 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
        *
345
        *
346
        * @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
        public void parseMatrix (String line) {
350
                txtInfo.setText("");
351
                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
                                if (next.equals("-1")) {
357
                                        txtInfo.append("-");
358
                                } else {
359
                                        txtInfo.append(next);
360
                                }
361

    
362
                                if (j < num - 1) {
363
                                        txtInfo.append(" ");
364
                                }
365
                        }
366

    
367
                        if (i < num - 1) {
368
                                txtInfo.append("\n");
369
                        }
370
                }
371
                repaint();
372
        }
373

    
374
        public void connect () {
375
    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
        setComponentsEnabled(true);
392
      }
393
    };
394
    SwingUtilities.invokeLater(r);
395
        }
396

    
397
        public void disconnect () {
398
    try {
399
        dataUpdater.interrupt();
400
    } catch (Exception e) {
401
    }
402
    csi = null;
403
                Runnable r = new Runnable() {
404
      public void run () {
405
                    btnConnect.setText("Connect");
406
              lblConnectionStatus.setText("Status: Disconnected");
407
        setComponentsEnabled(false);
408
        boundary = null;
409
        station = null;
410
            }
411
                };
412
                SwingUtilities.invokeLater(r);
413
        }
414
        
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
    btnCommand_ChargeNow.setEnabled(enabled);
426
    btnCommand_StopCharging.setEnabled(enabled);
427
    btnCommand_MoveTo.setEnabled(enabled);
428
    btnCommand_MoveAll.setEnabled(enabled);
429
        }
430

    
431
        /**
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
        public void parseQueue (String line) {
439

    
440
        }
441

    
442
        /**
443
        * Parses a String containing XBee ID values.
444
        * The ColonetServerInterface receives Strings of XBee information.        (For encoding
445
        * information, see the ColonetServerInterface documentation.)         This method takes
446
        * a string of the form "[command code] [command code] [number of robots] [id0] [id1] ..."
447
        * with tokens separated by spaces and containing no brackets.
448
        * The [command code]s are predefined values identifying this String as an XBee
449
        * ID String, [number of robots] is an integer, and the values that follow are
450
        * 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
        * The purpose of having this list is to ensure that robots are properly identified for control purposes.
453
        * This keeps robot identification consistent between sessions and prevents arbitrary assignment.
454
        *
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
        public void parseXBeeIDs (String line) {
460
                String [] str = line.split(" ");
461
                int num = Integer.parseInt(str[2]);
462
                xbeeID = new int[num];
463
                for (int i = 0; i < num; i++) {
464
                        xbeeID[i] = Integer.parseInt(str[i+3]);
465
                }
466
        }
467

    
468
        /**
469
        * Parses a String containing battery information.
470
        * The ColonetServerInterface receives Strings of battery information.         (For encoding
471
        * information, see the ColonetServerInterface documentation.)         This method takes
472
        * a string of the form "[command code] [command code] [robot ID] [value]"
473
        * with tokens separated by spaces and containing no brackets.
474
        * 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
        *
479
        * @param line the String containing battery information.
480
        * @see ColonetServerInterface#sendBatteryRequest(int)
481
        */
482
        public void parseBattery (String line) {
483
          int botNum, level;
484
          try {
485
                    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

    
493
                RobotIcon r = robotIcons.get(botNum);
494
                if (r != null) {
495
                    r.setBattery(batteryIcon.convert(level));  // set contextual battery meter
496
                    batteryIcon.setLevel(level);             // set solo battery meter
497
                }
498
                super.repaint();
499
        }
500

    
501
        /**
502
        * Parses a String containing visual robot position information along with
503
        * canonical ID assignments.
504
        */
505
        public void parsePositions (String line) {
506
                String [] str = line.split(" ");
507
                RobotList newList = new RobotList();
508

    
509
                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
                        RobotIcon newIcon = new RobotIcon(id, x, y);
514
                        // Save previous robot information
515
                        RobotIcon oldIcon = robotIcons.get(id);
516
                        if (oldIcon != null) {
517
                    newIcon.setBattery(oldIcon.getBattery());
518
                                newIcon.setDestination(oldIcon.getDestination());
519
                        }
520
                        if (newIcon.getID() >= 0) {
521
                                newIcon.setColor(Color.GREEN);
522
                        }
523
                        newList.put(id, newIcon);
524
                }
525
                robotIcons = newList;
526
                repaint();
527
        }
528
  
529
  /**
530
    * Parses a message that indicates a robot has reached its destination
531
   */
532
  public void parseMoveUpdate (String line) {
533
    System.out.println("Got move update: " + line);
534
    String [] str = line.split(" ");
535
    int id = Integer.parseInt(str[1]);
536
    robotIcons.get(id).clearDestination();
537
  }
538
        
539
        /**
540
        * Set the ID of the selected robot
541
        */
542
        public void setSelectedBot (int id) {
543
            this.selectedBot = id;
544
        }
545
        
546
        /**
547
        * Returns the ID of the selected robot
548
        */
549
        public int getSelectedBot () {
550
            return this.selectedBot;
551
        }
552

    
553
        //
554
        // MouseListener methods
555
        //
556
        public void mousePressed(MouseEvent e) {
557
                //Start a new Thread to handle the MouseEvent
558
                (new MouseHandler(e)).start();
559
        }
560
        public void mouseExited(MouseEvent e) {
561
        }
562
        public void mouseEntered(MouseEvent e) {
563
        }
564
        public void mouseReleased(MouseEvent e) {
565
    (new MouseHandler(e)).start();
566
        }
567
        public void mouseClicked(MouseEvent e) {
568
        }
569
        public void mouseDragged(MouseEvent e) {
570
    (new MouseHandler(e)).start();
571
        }
572
        public void mouseMoved(MouseEvent e) {
573
        }
574

    
575
        //
576
        // KeyListener methods
577
        //
578
        public void keyPressed (KeyEvent e) {
579
                //Start a new Thread to handle the KeyEvent
580
                (new KeyHandler(e)).start();
581
                repaint();
582
        }
583
        public void keyReleased (KeyEvent e) {
584
        }
585
        public void keyTyped (KeyEvent e) {
586
        }
587

    
588
        //
589
        // ActionListener method
590
        //
591
        public void actionPerformed (ActionEvent e) {
592
                // Start a new Thread to handle the ActionEvent
593
                (new ActionHandler(e)).start();
594
                repaint();
595
        }
596

    
597
        class MouseHandler extends Thread {
598
                MouseEvent e;
599

    
600
                public MouseHandler (MouseEvent event) {
601
                        super("MouseHandler");
602
                        this.e = event;
603
                }
604

    
605
                public void run () {
606
                        Point pt = panelWebcam.convertClick(e);
607

    
608
                        // If we are selecting a waypoint (destination) for a specific bot
609
                        if (setWaypoint && setWaypointID >= 0 && e.getID() == MouseEvent.MOUSE_PRESSED) {
610
                          // If the user clicks outside the boundary, do nothing
611
                          if (boundary.isActive() && !boundary.contains(e.getX(), e.getY())) {
612
                            return;
613
                          }
614
                                setWaypoint = false;
615
                                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
616
                                if (selectedBot < 0) {
617
                                        return;
618
                                }
619
                                
620
                                RobotIcon r = robotIcons.get(selectedBot);
621
                                if (r != null) {
622
                                        r.setDestination(pt);
623
                                        if (csi != null) {
624
                                                csi.sendAbsoluteMove(r.getID(), pt.x, pt.y);
625
                                        }
626
                                }
627
                                return;
628
                        }
629
                        
630
                        // If we are ordering a charge (right-click on a station)
631
                        if ((e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3)  
632
          && e.getID() == MouseEvent.MOUSE_PRESSED
633
          && station != null && station.contains(e.getX(), e.getY())) {
634
        if (selectedBot >= 0) {
635
          RobotIcon r = robotIcons.get(selectedBot);
636
          if (r != null) {
637
            Point p = panelWebcam.convertPoint(station.getX(), station.getY());
638
            r.setDestination(p);
639
          }
640
          csi.sendRecharge(selectedBot);
641
        }
642
                                return;
643
                        }
644

    
645
                        // Right-click also means we are moving a robot
646
                        if ((e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3)  
647
          && e.getID() == MouseEvent.MOUSE_PRESSED) {
648
        // If the user clicks outside the boundary, do nothing
649
                          if (boundary.isActive() && !boundary.contains(e.getX(), e.getY())) {
650
                            return;
651
                          }
652
                                if (selectedBot < 0) {
653
                                        return;
654
                                }
655
                                RobotIcon r = robotIcons.get(selectedBot);
656
                                if (r != null) {
657
                                        r.setDestination(pt);
658
                                        if (csi != null) {
659
                                                csi.sendAbsoluteMove(r.getID(), pt.x, pt.y);
660
                                        }
661
                                }
662
                                return;
663
                        }
664

    
665
                        // If we are setting all waypoints
666
                        if (setWaypoint && e.getID() == MouseEvent.MOUSE_PRESSED) {
667
                          // If the user clicks outside the boundary, do nothing
668
                          if (boundary.isActive() && !boundary.contains(e.getX(), e.getY())) {
669
                            return;
670
                          }
671
                                setWaypoint = false;
672
                                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
673
                                for (Map.Entry<Integer,RobotIcon> entry : robotIcons.entrySet()) {
674
                                        RobotIcon r = entry.getValue();
675
                                        r.setDestination(pt);
676
          //TODO: send absolute move commands
677
                                }
678
                                return;
679
                        }
680
      
681
      // If we are drawing a boundary rectangle
682
      if (boundary != null && boundary.isSetting()) {
683
        // Begin 
684
        if (e.getID() == MouseEvent.MOUSE_PRESSED) {
685
          boundary.panel_p1 = new Point(e.getX(), e.getY());
686
          boundary.panel_p2 = new Point(e.getX(), e.getY());
687
          boundary.img_p1 = new Point(pt.x, pt.y);
688
        }
689
        // Resize
690
        else if (e.getID() == MouseEvent.MOUSE_DRAGGED){
691
          boundary.panel_p2 = new Point(e.getX(), e.getY());
692
        }
693
        // Finish
694
        else if (e.getID() == MouseEvent.MOUSE_RELEASED){
695
          boundary.panel_p2 = new Point(e.getX(), e.getY());
696
          boundary.img_p2 = new Point(pt.x, pt.y);
697
          panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
698
          boundary.set = false;
699
          boundary.active = true;
700
          boundary.sendToServer();
701
        }
702
        return;
703
      }
704
      
705
      // If we are locating a charging station
706
      if (setStation) {
707
        setStation = false;
708
        station = new ChargingStation(e.getX(), e.getY());
709
        station.setActive(true);
710
        return;
711
      }
712

    
713
                        // Otherwise, we are selecting a bot, or doing nothing
714
                        RobotIcon r = robotIcons.getBoundingIcon(pt);
715
      if (r != null)
716
        selectedBot = r.getID();
717
                }
718
        }
719

    
720
        class KeyHandler extends Thread {
721
                KeyEvent e;
722

    
723
                public KeyHandler (KeyEvent event) {
724
                        super("KeyHandler");
725
                        this.e = event;
726
                }
727

    
728
                public void run () {
729
                        int code = e.getKeyCode();
730
                        if (code == KeyEvent.VK_UP) {
731
                                vectorController.setMaxForward();
732
                                vectorController.sendToServer();
733
                        } else if (code == KeyEvent.VK_DOWN) {
734
                                vectorController.setMaxReverse();
735
                                vectorController.sendToServer();
736
                        } else if (code == KeyEvent.VK_LEFT) {
737
                                vectorController.setMaxLeft();
738
                                vectorController.sendToServer();
739
                        } else if (code == KeyEvent.VK_RIGHT) {
740
                                vectorController.setMaxRight();
741
                                vectorController.sendToServer();
742
                        } else if (code == KeyEvent.VK_S) {
743
                                vectorController.setZero();
744
                                vectorController.sendToServer();
745
                        }
746
                }
747
        }
748

    
749
        class ActionHandler extends Thread {
750
                ActionEvent e;
751

    
752
                public ActionHandler (ActionEvent event) {
753
                        super("ActionHandler");
754
                        this.e = event;
755
                }
756

    
757
                public void run () {
758
                        Object source = e.getSource();
759

    
760
                        // General Actions
761
                        if (source == btnConnect) {
762
                                if (csi == null) {
763
                                        connect();
764
                                } else {
765
                                        disconnect();
766
                                }
767
                        } else if (source == btnGetXBeeIDs) {
768
                                csi.sendXBeeIDRequest();
769
                        } else if (source == btnAssignID) {
770
                                String message;
771
                                if (selectedBot < 0) {
772
                                        message = "That robot is unidentified. Please specify its ID.";
773
                                } else {
774
                                        message = "That robot has ID " + selectedBot + ". You may reassign it now.";
775
                                }
776
                                String result = JOptionPane.showInputDialog(self, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
777
                                if (result == null) {
778
                                        return;
779
                                }
780
                                int newID = -1;
781
                                try {
782
                                        newID = Integer.parseInt(result);
783
                                } catch (Exception ex) {
784
                                        csi.warn("Invalid ID.");
785
                                        return;
786
                                }
787
                                // Assign new ID and update display
788
                                if (csi != null) {
789
                                        csi.sendIDAssignment(selectedBot, newID);
790
                                }
791
                                selectedBot = newID;
792
                                lblSelected.setText("" + newID);
793
                  } else if (source == btnLocateStation) {
794
                    setStation = true;
795
                        } else if (source == btnF) { // Robot Movement Controls
796
                                vectorController.setMaxForward();
797
                                vectorController.sendToServer();
798
                                robotIcons.get(selectedBot).clearDestination();
799
                        } else if (source == btnB) {
800
                                vectorController.setMaxReverse();
801
                                vectorController.sendToServer();
802
                                robotIcons.get(selectedBot).clearDestination();
803
                        } else if (source == btnL) {
804
                                vectorController.setMaxLeft();
805
                                vectorController.sendToServer();
806
                                robotIcons.get(selectedBot).clearDestination();
807
                        } else if (source == btnR) {
808
                                vectorController.setMaxRight();
809
                                vectorController.sendToServer();
810
                                robotIcons.get(selectedBot).clearDestination();
811
                        } else if (source == btnActivate) {
812
                                vectorController.setZero();
813
                                vectorController.sendToServer();
814
                                robotIcons.get(selectedBot).clearDestination();
815
      } else if (source == btnSetBounds) {
816
        boundary.set = true;
817
        panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
818
      } else if (source == btnClearBounds) {
819
        boundary.active = false;
820
        boundary.set = false;
821
        boundary.img_p1 = new Point(-1, -1);
822
        boundary.img_p2 = new Point(-1, -1);
823
        boundary.panel_p1 = new Point(-1, -1);
824
        boundary.panel_p2 = new Point(-1, -1);
825
        boundary.sendToServer();
826
                        } else if (source == btnCommand_MoveTo) {
827
                                if (selectedBot < 0) {
828
                                        return;
829
                                }
830
                                panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
831
                                setWaypoint = true;
832
                                setWaypointID = selectedBot;
833
                        } else if (source == btnCommand_MoveAll) {
834
                                panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
835
                                setWaypoint = true;
836
                                setWaypointID = -1;
837
                        } else if (source == btnCommand_StopTask) {
838

    
839
                        } else if (source == btnCommand_ResumeTask) {
840

    
841
                        } else if (source == btnCommand_ChargeNow) {
842
                          if (selectedBot >= 0)
843
                            csi.sendRecharge(selectedBot);
844
                          else
845
          csi.sendRecharge(2);
846
                        } else if (source == btnCommand_StopCharging) {
847
                          if (selectedBot >= 0) {
848
                            robotIcons.get(selectedBot).clearDestination();
849
                            csi.sendRechargeStop(selectedBot);
850
                          } else {
851
                            robotIcons.get(2).clearDestination();
852
          csi.sendRechargeStop(2);
853
        }
854
                        } else if (source == btnAddTask) { // Queue Management
855
                                taskAddWindow.prompt();
856
                        } else if (source == btnRemoveTask) {
857
                                if (taskList.getSelectedIndex() >= 0) {
858
                                        csi.sendQueueRemove(taskList.getSelectedIndex());
859
                                }
860
                                csi.sendQueueUpdate();
861
                        } else if (source == btnMoveTaskUp) {
862
                                csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
863
                                csi.sendQueueUpdate();
864
                        } else if (source == btnMoveTaskDown) {
865
                                csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
866
                                csi.sendQueueUpdate();
867
                        } else if (source == btnUpdateTasks) {
868
                                csi.sendQueueUpdate();
869
                        }
870

    
871
                }
872
        }
873

    
874
        /*
875
        * DataUpdater thread.
876
        *        The purpose of this thread is to request data from the server at regular intervals.
877
        *        Types of data requested: XBee IDs, robot positions, battery levels.
878
        *
879
        */
880
        class DataUpdater extends Thread {
881
                final int DATAUPDATER_DELAY = 500;
882
                int count;
883
                public DataUpdater () {
884
                        super("Colonet DataUpdater");
885
                        count = 0;
886
                }
887

    
888
                public synchronized void run () {
889
                        while (true) {
890
                                try {
891
                                        if (csi != null && csi.isReady()) {
892
                                                // XBee ID Request
893
                                                if (count % 5 == 0) {
894
                                                        csi.sendXBeeIDRequest();
895
                                                }
896
                                                // Robot Position Request
897
                                                if (count % 1 == 0) {
898
                                                        csi.sendPositionRequest();
899
                                                }
900
                                                // Battery Request
901
                                                if (count % 30 == 0) {
902
                                                        for (Map.Entry<Integer,RobotIcon> entry : robotIcons.entrySet()) {
903
                                                                RobotIcon r = entry.getValue();
904
                                                                        int id = r.getID();
905
                                                                                if (id >= 0) {
906
                                                                                csi.sendBatteryRequest(id);
907
                                                                        }
908
                                                        }
909
                                                }
910
                                        }
911
                                        count++;
912
                                        if (count > 1000)
913
                                                count = 0;
914
                                        Thread.sleep(DATAUPDATER_DELAY);
915
                                } catch (InterruptedException e) {
916
                                        return;
917
                                } catch (NullPointerException e) {
918
                                    // This probably indicates that an object was destroyed by shutdown.
919
                                    return;
920
                                }
921
                        }
922
                }
923
        }
924

    
925

    
926
        /*
927
        * WebcamPanel class
928
        * Contains the webcam image
929
        */
930
        class WebcamPanel extends JPanel {
931

    
932
                volatile BufferedImage img;
933
                BufferedImage buffer;
934

    
935
                public WebcamPanel () {
936
                        super(true);
937
                }
938

    
939
                public synchronized void setImage (BufferedImage newimg) {
940
                        if (img != null) {
941
                                img.flush();
942
                        }
943
                        System.gc();
944
                        img = newimg;
945
                        repaint();
946
                }
947

    
948
                public synchronized void paint (Graphics g) {
949
                        if (img == null) {
950
                                return;
951
                        }
952

    
953
                        // Calculate scaling
954
                        int maxWidth = getWidth() - 2*ColonetConstants.WEBCAM_BORDER;
955
                        int maxHeight = getHeight() - 2*ColonetConstants.WEBCAM_BORDER;
956
                        double widthRatio = 1.0 * maxWidth / img.getWidth();
957
                        double heightRatio = 1.0 * maxHeight / img.getHeight();
958
                        double scale = 0;
959
                        int newWidth = 0;
960
                        int newHeight = 0;
961
                        int x = 0;
962
                        int y = 0;
963

    
964
                        if (widthRatio > heightRatio) {         //height is the limiting factor
965
                                scale = heightRatio;
966
                                newHeight = maxHeight;
967
                                newWidth = (int) (img.getWidth() * scale);
968
                                y = ColonetConstants.WEBCAM_BORDER;
969
                                x = (maxWidth - newWidth) / 2 + ColonetConstants.WEBCAM_BORDER;
970
                        } else {        //width is the limiting factor
971
                                scale = widthRatio;
972
                                newWidth = maxWidth;
973
                                newHeight = (int) (img.getHeight() * scale);
974
                                x = ColonetConstants.WEBCAM_BORDER;
975
                                y = (maxHeight - newHeight) / 2 + ColonetConstants.WEBCAM_BORDER;
976
                        }
977

    
978
                        // Draw image onto the buffer
979
                        buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
980
                        Graphics2D bufferedGraphics = (Graphics2D)buffer.getGraphics();
981
                        bufferedGraphics.setColor(Color.GRAY);
982
                        bufferedGraphics.fillRect(0, 0, this.getWidth(), this.getHeight());
983
                        Image imgScaled = img.getScaledInstance(newWidth, newHeight, Image.SCALE_FAST);
984
                        bufferedGraphics.drawImage(imgScaled, x, y, this);
985

    
986
      // Draw boundary
987
      if (boundary != null && (boundary.isSetting() || boundary.isActive())) {
988
         bufferedGraphics.setColor(Color.BLUE);
989
         int width = boundary.panel_p2.x - boundary.panel_p1.x;
990
         int height = boundary.panel_p2.y - boundary.panel_p1.y;
991
         bufferedGraphics.drawRect(boundary.panel_p1.x, boundary.panel_p1.y, width, height);
992
      }
993
      
994
      // Draw charging station
995
      if (station != null && station.isActive()) {
996
        bufferedGraphics.setStroke(new BasicStroke(2));
997
        bufferedGraphics.setColor(Color.ORANGE);
998
        int width = ColonetConstants.STATION_SIZE;
999
        bufferedGraphics.drawRect(station.getX() - width/2, station.getY() - width/2, width, width);
1000
      }
1001

    
1002
                        // Draw Identifiers and battery levels
1003
                        if (robotIcons != null) {
1004
                                bufferedGraphics.setStroke(new BasicStroke(2));
1005
                                for (Map.Entry<Integer,RobotIcon> entry : robotIcons.entrySet()) {
1006
                                        RobotIcon r = entry.getValue();
1007
                                        bufferedGraphics.setColor(r.getColor());
1008
                                        // Identifier circle
1009
                                        int px = (int) (x + r.getLocation().x * scale);
1010
                                        int py = (int) (y + r.getLocation().y * scale);
1011
                                        int radius = ColonetConstants.ROBOT_RADIUS;
1012
                                        bufferedGraphics.drawOval(px-radius, py-radius, 2*radius, 2*radius);
1013
                                        // ID, if applicable
1014
                                        if (r.getID() > 0) {
1015
                                            bufferedGraphics.setFont(new Font("arial", Font.PLAIN, 36));
1016
                                            bufferedGraphics.drawString("" + r.getID(), px-10, py+10);
1017
                                        }
1018
                                        // Battery
1019
                                        if (r.getBattery() >= 0) {
1020
                                            int pixels = r.getBattery() * ColonetConstants.BATTERY_WIDTH / 100;
1021
                                                if (r.getBattery() > 50)
1022
                                                    bufferedGraphics.setColor(Color.GREEN);
1023
                                                else if (r.getBattery() > 25)
1024
                                                    bufferedGraphics.setColor(Color.YELLOW);
1025
                                                else
1026
                                                    bufferedGraphics.setColor(Color.RED);
1027
                                                bufferedGraphics.fillRect(px+20, py+20, pixels, ColonetConstants.BATTERY_HEIGHT);
1028
                                                bufferedGraphics.setColor(Color.BLACK);
1029
                                                bufferedGraphics.drawRect(px+20, py+20, ColonetConstants.BATTERY_WIDTH, ColonetConstants.BATTERY_HEIGHT);
1030
                                        }
1031
                                        // If the robot has a destination, draw the vector
1032
                                        if (r.hasDestination()) {
1033
                                                bufferedGraphics.drawLine(px, py, (int)(x + r.getDestination().x * scale), (int)(y + r.getDestination().y * scale));
1034
                                        }
1035
                                }
1036
                        }
1037

    
1038
                        // Identify currently-selected robot
1039
                        RobotIcon r = robotIcons.get(selectedBot);
1040
                        if (r != null) {
1041
                                int px = (int) (x + r.getLocation().x * scale);
1042
                                int py = (int) (y + r.getLocation().y * scale);
1043
                                int radius = ColonetConstants.ROBOT_RADIUS;
1044
                                bufferedGraphics.setColor(Color.BLACK);
1045
                                bufferedGraphics.drawOval(px-radius-6, py-radius-6, 2*radius+12, 2*radius+12);
1046
                        }
1047

    
1048
                        //Display buffered content
1049
                        g.drawImage(buffer, 0, 0, this);
1050
                }
1051

    
1052
                /**
1053
                * Convert a click on the webcam panel to a coordinate that is consistent with the
1054
                * original size of the image that the panel contains.
1055
                */
1056
                public Point convertClick (MouseEvent e) {
1057
                        if (img == null) {
1058
                                return new Point(e.getX(), e.getY());
1059
                        }
1060
                        return convertPoint(e.getX(), e.getY());
1061
                }
1062
                
1063
                /**
1064
                * Convert a point on the webcam panel to a coordinate that is consistent with the
1065
                * original size of the image that the panel contains.
1066
                */
1067
                public Point convertPoint (int panelx, int panely) {
1068

    
1069
                        // Calculate scaling
1070
                        int border = ColonetConstants.WEBCAM_BORDER;
1071
                        int clickx = panelx;
1072
                        int clicky = panely;
1073
                        int maxWidth = getWidth() - 2*border;
1074
                        int maxHeight = getHeight() - 2*border;
1075
                        double widthRatio = 1.0 * maxWidth / img.getWidth();
1076
                        double heightRatio = 1.0 * maxHeight / img.getHeight();
1077
                        double scale = 0;
1078
                        int newWidth = 0;
1079
                        int newHeight = 0;
1080
                        int px = 0;
1081
                        int py = 0;
1082

    
1083
                        if (widthRatio > heightRatio) {         //height is the limiting factor
1084
                                scale = heightRatio;
1085
                                newHeight = maxHeight;
1086
                                newWidth = (int) (img.getWidth() * scale);
1087
                                py = clicky - border;
1088
                                px = clickx - border - (maxWidth - newWidth) / 2;
1089
                        } else {        //width is the limiting factor
1090
                                scale = widthRatio;
1091
                                newWidth = maxWidth;
1092
                                newHeight = (int) (img.getHeight() * scale);
1093
                                px = clickx - border;
1094
                                py = clicky - border - (maxHeight - newHeight) / 2;
1095
                        }
1096
                        py = (int) (py / scale);
1097
                        px = (int) (px / scale);
1098
                        return new Point(px, py);
1099
                }
1100
                
1101
        }
1102

    
1103
        /*
1104
        * WebcamLoader class
1105
        * Handles the loading of the webcam image.
1106
        */
1107
        class WebcamLoader extends Thread
1108
        {
1109
                URL imagePath;
1110
                MediaTracker mt;
1111
                BufferedImage image;
1112
                Random rand;
1113

    
1114
                public WebcamLoader (JApplet applet)
1115
                {
1116
                        super("ColonetWebcamLoader");
1117
                        mt = new MediaTracker(applet);
1118
                        ImageIO.setUseCache(false);
1119
                        rand = new Random();
1120
                }
1121

    
1122
                public void run ()
1123
                {
1124
                        while (true) {
1125
                                try {
1126
                                        Thread.sleep(ColonetConstants.WEBCAM_DELAY);
1127
                                        if (image != null)
1128
                                                image.flush();
1129
                                        System.gc();
1130
                                        try {
1131
                                                imagePath = new URL(ColonetConstants.WEBCAM_PATH + "?rand=" + rand.nextInt(100000));
1132
                                        } catch (MalformedURLException e) {
1133
                                                System.out.println("Malformed URL: could not form URL from: [" + ColonetConstants.WEBCAM_PATH + "]\n");
1134
                                        }
1135
                                        image = ImageIO.read(imagePath);
1136
                                        // The MediaTracker waitForID pauses the thread until the image is loaded.
1137
                                        // We don't want to display a half-downloaded image.
1138
                                        mt.addImage(image, 1);
1139
                                        while(!mt.checkID(1))
1140
                                                Thread.sleep(20);
1141
                                        mt.removeImage(image);
1142
                                        // Save
1143
                                        panelWebcam.setImage(image);
1144
                                } catch (InterruptedException e) {
1145
                                        return;
1146
                                } catch (java.security.AccessControlException e) {
1147
                                        csi.warn("Could not load webcam.\n" + e);
1148
                                        return;
1149
                                } catch (IOException e) {
1150
                                        getInfoPanel().append("IOException while trying to load image.");
1151
                                }
1152
                        }
1153
                }
1154
        }
1155
  
1156
  class RobotBoundary {
1157
    public volatile boolean active, set;
1158
    public volatile Point img_p1, img_p2;
1159
    public volatile Point panel_p1, panel_p2;
1160
    
1161
    public RobotBoundary () {
1162
      active = false;
1163
      set = false;
1164
      img_p1 = new Point(-1,-1);
1165
      img_p2 = new Point(-1,-1);
1166
      panel_p1 = new Point(-1,-1); 
1167
      panel_p2 = new Point(-1,-1);
1168
    }
1169
    
1170
    public void sendToServer () {
1171
      if (csi != null) {
1172
        if (img_p1.x == -1 || img_p1.y == -1)
1173
          csi.sendBoundaryClear();
1174
        else
1175
          csi.sendBoundary(img_p1.x, img_p1.y, img_p2.x, img_p2.y);
1176
      }
1177
    }
1178
    
1179
    public boolean isActive () {
1180
      return active;
1181
    }
1182
    
1183
    public boolean isSetting () {
1184
      return set;
1185
    }
1186
    
1187
    /**
1188
    *  Returns a Rectangle designating the active area 
1189
    *  in the coordinate system of the JPanel.
1190
    *  Returns null if the boundary is not active.
1191
    */
1192
    public Rectangle toRectangle () {
1193
      if (!active)
1194
        return null;
1195
      return new Rectangle (panel_p1.x, panel_p1.y, (panel_p2.x-panel_p1.x), (panel_p2.y-panel_p1.y));
1196
    }
1197
    
1198
    /** 
1199
    *  Determines whether a coordinate in the coordinate
1200
    *  system of the JPanel is contained within the boundary.
1201
    *  If the boundary is not active, then this method 
1202
    *  returns false.
1203
    */
1204
    public boolean contains (int px, int py) {
1205
      Rectangle rect = this.toRectangle();
1206
      if (rect == null || !this.active)
1207
        return false;
1208
      return rect.contains(px, py);
1209
    }
1210
        
1211
  }
1212
  
1213
  class ChargingStation {
1214
    private int x, y;
1215
    private boolean active;
1216
    
1217
    public ChargingStation () {
1218
      super();
1219
    }
1220
    
1221
    public ChargingStation (int x, int y) {
1222
      this();
1223
      setLocation(x, y);
1224
    }
1225
    
1226
    public int getX () {
1227
      return x;
1228
    }
1229
    
1230
    public int getY () {
1231
      return y;
1232
    }
1233
    
1234
    /**
1235
    *  Sets the center of the charging station to
1236
    *  the specified coordinate point.
1237
    */
1238
    public void setLocation (int x, int y) {
1239
      this.x = x;
1240
      this.y = y;
1241
    }
1242
    
1243
    public boolean isActive () {
1244
      return active;
1245
    }
1246
    
1247
    public void setActive (boolean status) {
1248
      this.active = status;
1249
    }
1250
    
1251
    /**
1252
    *  Determines whether a coordinate is contained within
1253
    *  the graphical area of the charging station.
1254
    *  Returns false if the charging station is not active.
1255
    */
1256
    public boolean contains (int px, int py) {
1257
      if (!active)
1258
        return false;
1259
      int width = ColonetConstants.STATION_SIZE;
1260
      Rectangle rect = new Rectangle(this.x - width/2, this.y - width/2, width, width);
1261
      return rect.contains(px, py);
1262
    }
1263
  
1264
  }
1265

    
1266
}