Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (40.1 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, Runnable {
19

    
20
        // Used for painting
21
        final int BUFFER = 50;
22
        final int RADIUS = 30;
23

    
24
        // Used for the robot controller
25
        final int VECTOR_CONTROLLER_HEIGHT = 190;
26
        final int VECTOR_CONTROLLER_WIDTH = 320;
27

    
28
        // Connection
29
        JTextField txtHost;
30
        JTextField txtPort;
31
        JButton btnConnect;
32
        JButton btnGetXBeeIDs;
33
        JLabel lblConnectionStatus;
34
        JTextArea txtInfo;
35
        JPanel panelConnect;
36
        JPanel panelServerInterface;
37
        Socket socket;
38
        DataUpdater dataUpdater;
39

    
40
        // Control
41
        JPanel panelControl;
42
        JTabbedPane tabPaneControl;
43
        JPanel panelRobotControl;
44
        JPanel panelRobotDirection;
45
        JPanel panelRobotDirectionButtons;
46
        JPanel panelRobotCommands;
47
        JButton btnF, btnB, btnL, btnR, btnActivate;
48
        JComboBox cmbRobotNum;
49
        JLabel lblBattery;
50
        JLabel lblSelected;
51
        BatteryIcon batteryIcon;
52
        JPanel panelBattery;
53
        VectorController vectorController;
54
        BufferedImage imageVectorControl;
55
        JButton btnAssignID;
56
        boolean setWaypoint;
57
        int setWaypointID;
58
        JButton btnCommand_MoveTo;
59
        JButton btnCommand_MoveAll;
60
        JButton btnCommand_StopTask;
61
        JButton btnCommand_ResumeTask;
62
        JButton btnCommand_ChargeNow;
63
        JButton btnCommand_StopCharging;
64

    
65
        // Task Manager
66
        JPanel panelTaskManager;
67
        JScrollPane spTaskManager;
68
        JPanel panelTaskManagerControls;
69
        JPanel panelTaskManagerControlsPriority;
70
        DefaultListModel taskListModel;
71
        JList taskList;
72
        JButton btnAddTask;
73
        JButton btnRemoveTask;
74
        JButton btnMoveTaskUp;
75
        JButton btnMoveTaskDown;
76
        JButton btnUpdateTasks;
77
        TaskAddWindow taskAddWindow;
78

    
79
        // Webcam
80
        WebcamPanel panelWebcam;
81
        GraphicsConfiguration gc;
82
        JTabbedPane tabPaneMain;
83
        WebcamLoader webcamLoader;
84
        
85
        // Robots
86
        volatile int selectedBot;         //the user has selected this bot graphically
87
        volatile java.util.Map <Integer, RobotIcon> robotIcons;         //contains boundary shapes around bots for click detection
88
        volatile int[] xbeeID;
89

    
90
        Colonet self = this;
91
        volatile ColonetServerInterface csi;
92
        Thread paintThread;
93

    
94
        public void init () {
95
                // Set the default look and feel - choose one
96
                String laf = UIManager.getSystemLookAndFeelClassName();
97
                //String laf = UIManager.getCrossPlatformLookAndFeelClassName();
98
                //String laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
99
                try {
100
                        UIManager.setLookAndFeel(laf);
101
                } catch (UnsupportedLookAndFeelException exc) {
102
                        System.err.println ("Warning: UnsupportedLookAndFeel: " + laf);
103
                } catch (Exception exc) {
104
                        System.err.println ("Error loading " + laf + ": " + exc);
105
                }
106

    
107
                // We should invoke and wait to avoid browser display difficulties
108
                Runnable r = new Runnable() {
109
                        public void run() {
110
                                createAndShowGUI();
111
                        }
112
                };
113

    
114
                try {
115
                        SwingUtilities.invokeAndWait(r);
116
                } catch (InterruptedException e) {
117
                        //Not really sure why we would be in this situation
118
                        System.out.println("InterruptedException in init: " + e);
119
                } catch (java.lang.reflect.InvocationTargetException e) {
120
                        //This could happen for various reasons if there is a problem in createAndShowGUI
121
                        e.printStackTrace();
122
                }
123
        }
124

    
125
        public void destroy () {
126
                if (csi != null) {
127
                        csi.disconnect();
128
                }
129
        }
130

    
131
        private synchronized void createAndShowGUI () {
132
                paintThread = new Thread(this, "PaintThread");
133
                paintThread.start();
134
                
135
                // Webcam
136
                gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
137
                panelWebcam = new WebcamPanel();
138
                tabPaneMain = new JTabbedPane();
139
                tabPaneMain.setFont(new Font("arial", Font.PLAIN, 16));
140
                tabPaneMain.add(panelWebcam, "Webcam");
141

    
142
                // Robots
143
                selectedBot = -1;
144
                robotIcons = new HashMap <Integer, RobotIcon> ();
145

    
146
                // Connection area
147
                txtInfo = new JTextArea();
148
                txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
149
                txtHost = new JTextField(this.getDocumentBase().getHost());
150
                txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
151
                txtPort = new JTextField("10123");
152
                txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
153
                btnConnect = new JButton("Connect");
154
                btnConnect.setFont(new Font("arial", Font.BOLD, 16));
155
                btnGetXBeeIDs = new JButton("Get XBee IDs");
156
                getRootPane().setDefaultButton(btnConnect);
157
                lblConnectionStatus = new JLabel("Status: Offline");
158
                panelConnect = new JPanel();
159
                panelConnect.setLayout(new GridLayout(6,1));
160
                panelConnect.add(lblConnectionStatus);
161
                panelConnect.add(txtHost);
162
                panelConnect.add(txtPort);
163
                panelConnect.add(btnConnect);
164
                //panelConnect.add(btnGetXBeeIDs);
165
                panelServerInterface = new JPanel();
166
                panelServerInterface.setLayout(new GridLayout(2,1));
167
                panelServerInterface.add(panelConnect);
168
                panelServerInterface.add(txtInfo);
169

    
170
                // Robot direction panel
171
                panelRobotDirection = new JPanel();
172
                panelRobotDirectionButtons = new JPanel();
173

    
174
                Font f = new Font(null, Font.PLAIN, 18);
175
                btnF = new JButton("\u2191");
176
                btnF.setFont(f);
177
                btnB = new JButton("\u2193");
178
                btnB.setFont(f);
179
                btnL = new JButton("\u2190");
180
                btnL.setFont(f);
181
                btnR = new JButton("\u2192");
182
                btnR.setFont(f);
183
                btnActivate = new JButton("\u25A0");
184
                btnActivate.setFont(new Font(null, Font.BOLD, 36));
185

    
186
                panelRobotDirectionButtons.setLayout(new GridLayout(1,5));
187
                panelRobotDirectionButtons.add(btnActivate);
188
                panelRobotDirectionButtons.add(btnF);
189
                panelRobotDirectionButtons.add(btnB);
190
                panelRobotDirectionButtons.add(btnL);
191
                panelRobotDirectionButtons.add(btnR);
192

    
193
                imageVectorControl = gc.createCompatibleImage(VECTOR_CONTROLLER_WIDTH, VECTOR_CONTROLLER_HEIGHT);
194
                vectorController = new VectorController(imageVectorControl);
195
                panelRobotDirection.setLayout(new BorderLayout());
196
                panelRobotDirection.add(vectorController, BorderLayout.CENTER);
197
                panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
198

    
199
                // Robot Control and Commands
200
                panelRobotCommands = new JPanel();
201
                panelRobotCommands.setLayout(new GridLayout(5,2));
202
                cmbRobotNum = new JComboBox();
203
                // Battery subset
204
                batteryIcon = new BatteryIcon(0);
205
                lblBattery = new JLabel(batteryIcon);
206
                lblSelected = new JLabel("None");
207
                // Command subset
208
                setWaypoint = false;
209
                setWaypointID = -1;
210
                btnAssignID = new JButton("Assign ID");
211
                btnCommand_MoveTo = new JButton("Move to ...");
212
                btnCommand_MoveAll = new JButton("Move all ...");
213
                btnCommand_StopTask = new JButton("Stop Current Task");
214
                btnCommand_ResumeTask = new JButton("Resume Current Task");
215
                btnCommand_ChargeNow = new JButton("Recharge Now");
216
                btnCommand_StopCharging = new JButton("Stop Recharging");
217
                panelRobotCommands.add(new JLabel("Select Robot to Control: "));
218
                panelRobotCommands.add(cmbRobotNum);
219
                panelRobotCommands.add(new JLabel("Battery Level: "));
220
                panelRobotCommands.add(lblBattery);
221
                panelRobotCommands.add(new JLabel("Selected Icon: "));
222
                panelRobotCommands.add(lblSelected);
223
                panelRobotCommands.add(btnAssignID);
224
                panelRobotCommands.add(new JLabel(""));
225
                panelRobotCommands.add(btnCommand_MoveTo);
226
                panelRobotCommands.add(btnCommand_MoveAll);
227
                //panelRobotCommands.add(btnCommand_StopTask);
228
                //panelRobotCommands.add(btnCommand_ResumeTask);
229
                //panelRobotCommands.add(btnCommand_ChargeNow);
230
                //panelRobotCommands.add(btnCommand_StopCharging);
231
                panelRobotControl = new JPanel();
232
                panelRobotControl.setLayout(new GridLayout(2,1));
233
                panelRobotControl.add(panelRobotDirection);
234
                panelRobotControl.add(panelRobotCommands);
235

    
236
                // Task Manager
237
                panelTaskManager = new JPanel();
238
                panelTaskManager.setLayout(new BorderLayout());
239
                taskListModel = new DefaultListModel();
240
                taskList = new JList(taskListModel);
241
                taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
242
                taskList.setSelectedIndex(0);
243
                spTaskManager = new JScrollPane(taskList);
244
                panelTaskManagerControls = new JPanel();
245
                panelTaskManagerControls.setLayout(new GridLayout(1,4));
246
                panelTaskManagerControlsPriority = new JPanel();
247
                panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
248
                btnAddTask = new JButton("Add...");
249
                btnRemoveTask = new JButton("Remove");
250
                btnMoveTaskUp = new JButton("^");
251
                btnMoveTaskDown = new JButton("v");
252
                btnUpdateTasks = new JButton("Update");
253
                panelTaskManagerControlsPriority.add(btnMoveTaskUp);
254
                panelTaskManagerControlsPriority.add(btnMoveTaskDown);
255
                panelTaskManagerControls.add(btnAddTask);
256
                panelTaskManagerControls.add(btnRemoveTask);
257
                panelTaskManagerControls.add(btnUpdateTasks);
258
                panelTaskManagerControls.add(panelTaskManagerControlsPriority);
259
                panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
260
                panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
261
                panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
262
                taskAddWindow = new TaskAddWindow();
263

    
264
                // Main control mechanism
265
                panelControl = new JPanel();
266
                panelControl.setLayout(new GridLayout(1,1));
267
                tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
268
                tabPaneControl.setFont(new Font("arial", Font.PLAIN, 16));
269
                tabPaneControl.setPreferredSize(new Dimension(VECTOR_CONTROLLER_WIDTH, 0));
270
                tabPaneControl.addTab("Connection", panelServerInterface);
271
                tabPaneControl.addTab("Robots", panelRobotControl);
272
                //tabPaneControl.addTab("Tasks", panelTaskManager);
273
                panelControl.add(tabPaneControl);
274

    
275

    
276
                // Put all elements in the ContentPane
277
                this.getContentPane().setLayout(new BorderLayout());
278
                this.getContentPane().add(tabPaneMain, BorderLayout.CENTER);
279
                this.getContentPane().add(panelControl, BorderLayout.EAST);
280
                this.setVisible(true);
281
                
282
                // Disable components before connecting
283
                btnConnect.setText("Connect");
284
        lblConnectionStatus.setText("Status: Disconnected");
285
        btnF.setEnabled(false);
286
        btnB.setEnabled(false);
287
        btnL.setEnabled(false);
288
        btnR.setEnabled(false);
289
        btnActivate.setEnabled(false);
290
        cmbRobotNum.setEnabled(false);
291
        btnAssignID.setEnabled(false);
292
        btnCommand_MoveTo.setEnabled(false);
293
        btnCommand_MoveAll.setEnabled(false);
294

    
295
                /* Add all listeners here */
296
                // Task Management
297
                btnAddTask.addActionListener(this);
298
                btnRemoveTask.addActionListener(this);
299
                btnMoveTaskUp.addActionListener(this);
300
                btnMoveTaskDown.addActionListener(this);
301
                btnUpdateTasks.addActionListener(this);
302
                // Robot Control
303
                btnF.addActionListener(this);
304
                btnB.addActionListener(this);
305
                btnL.addActionListener(this);
306
                btnR.addActionListener(this);
307
                btnF.addKeyListener(this);
308
                btnB.addKeyListener(this);
309
                btnL.addKeyListener(this);
310
                btnR.addKeyListener(this);
311
                btnActivate.addActionListener(this);
312
                btnActivate.addKeyListener(this);
313
                cmbRobotNum.addKeyListener(this);
314
                btnCommand_MoveTo.addActionListener(this);
315
                btnCommand_MoveAll.addActionListener(this);
316
                btnCommand_StopTask.addActionListener(this);
317
                btnCommand_ResumeTask.addActionListener(this);
318
                btnCommand_ChargeNow.addActionListener(this);
319
                btnCommand_StopCharging.addActionListener(this);
320
                // Other
321
                btnConnect.addActionListener(this);
322
                btnGetXBeeIDs.addActionListener(this);
323
                btnAssignID.addActionListener(this);
324
                panelWebcam.addMouseListener(this);
325
        }
326

    
327
        public void paint (Graphics g) {
328
                super.paint(g);
329
        }
330

    
331
        public void update (Graphics g) {
332
                paint(g);
333
        }
334
        
335
        public void run () {
336
                final int PAINT_DELAY = 300;
337
                while (true) {
338
                        try {
339
                                Thread.sleep(PAINT_DELAY);
340
                        } catch (Exception e) {
341
                                System.out.println(e);
342
                                return;
343
                        }
344
                        repaint();
345
                }
346
        }
347

    
348
        /**
349
        * Gets the JTextArea used for displaying debugging information. 
350
        *
351
        * @return the JTextArea where debugging information can be displayed.
352
        */
353
        public JTextArea getInfoPanel () {
354
                return txtInfo;
355
        }
356

    
357
        /**
358
        * Parses a String containing BOM matrix information.
359
        * The ColonetServerInterface receives lines of the BOM matrix.        (For encoding
360
        * information, see the ColonetServerInterface documentation.)         The entire matrix is passed
361
        * to the client when requested. This method takes a string of the form
362
        * "[command code] [command code] [number of robots] [data0] [data1] ..."
363
        * with tokens separated by spaces and containing no brackets.
364
        * The [command code]s are predefined values identifying this String as a BOM data
365
        * String, [number of robots] is an integer, and the values that follow are
366
        * the sensor readings of the robots in order, starting with robot 0.        Only [number of robots]^2
367
        * data entries will be read.        The matrix values are saved locally until the next String is parsed.
368
        *
369
        *
370
        * @param line the String containing BOM matrix information.
371
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots]^2 data entries in the String
372
        */
373
        public void parseMatrix (String line) {
374
                txtInfo.setText("");
375
                String [] str = line.split(" ");
376
                int num = Integer.parseInt(str[2]);
377
                for (int i = 0; i < num; i++) {
378
                        for (int j = 0; j < num; j++) {
379
                                String next = str[3 + i*num + j];
380
                                if (next.equals("-1")) {
381
                                        txtInfo.append("-");
382
                                } else {
383
                                        txtInfo.append(next);
384
                                }
385

    
386
                                if (j < num - 1) {
387
                                        txtInfo.append(" ");
388
                                }
389
                        }
390

    
391
                        if (i < num - 1) {
392
                                txtInfo.append("\n");
393
                        }
394
                }
395
                repaint();
396
        }
397

    
398
        public void connect () {
399
        if (csi != null)
400
                return;
401
        csi = new ColonetServerInterface(self);
402
        csi.connect(txtHost.getText(), txtPort.getText());
403
        if (!csi.isReady()) {
404
                csi = null;
405
                return;
406
        }
407
        webcamLoader = new WebcamLoader(self);
408
        dataUpdater = new DataUpdater();
409
        dataUpdater.start();
410
        webcamLoader.start();
411
        Runnable r = new Runnable() {
412
                public void run () {
413
                        btnConnect.setText("Disconnect");
414
                        lblConnectionStatus.setText("Status: Connected");
415
                btnF.setEnabled(true);
416
                btnB.setEnabled(true);
417
                btnL.setEnabled(true);
418
                btnR.setEnabled(true);
419
                btnActivate.setEnabled(true);
420
                cmbRobotNum.setEnabled(true);
421
                btnAssignID.setEnabled(true);
422
                btnCommand_MoveTo.setEnabled(true);
423
                btnCommand_MoveAll.setEnabled(true);
424
                    }
425
                };
426
                SwingUtilities.invokeLater(r);
427
        }
428

    
429
        public void disconnect () {
430
        try {
431
            dataUpdater.interrupt();
432
        } catch (Exception e) {
433
        }
434
        csi = null;
435
                Runnable r = new Runnable() {
436
                public void run () {
437
                        btnConnect.setText("Connect");
438
                    lblConnectionStatus.setText("Status: Disconnected");
439
                btnF.setEnabled(false);
440
                btnB.setEnabled(false);
441
                btnL.setEnabled(false);
442
                btnR.setEnabled(false);
443
                btnActivate.setEnabled(false);
444
                cmbRobotNum.setEnabled(false);
445
                btnAssignID.setEnabled(false);
446
                btnCommand_MoveTo.setEnabled(false);
447
                btnCommand_MoveAll.setEnabled(false);
448
                    }
449
                };
450
                SwingUtilities.invokeLater(r);
451
        }
452

    
453
        /**
454
        * Parses a String containing a task queue update.
455
        * Format is currently not specified.
456
        * This method currently does nothing.
457
        *
458
        * @param line the String containing task queue update information.
459
        */
460
        public void parseQueue (String line) {
461

    
462
        }
463

    
464
        /**
465
        * Parses a String containing XBee ID values.
466
        * The ColonetServerInterface receives Strings of XBee information.        (For encoding
467
        * information, see the ColonetServerInterface documentation.)         This method takes
468
        * a string of the form "[command code] [command code] [number of robots] [id0] [id1] ..."
469
        * with tokens separated by spaces and containing no brackets.
470
        * The [command code]s are predefined values identifying this String as an XBee
471
        * ID String, [number of robots] is an integer, and the values that follow are
472
        * the IDs of the robots in order, starting with robot 0.        Only [number of robots]
473
        * will be read.         The ID values are saved locally until the next String is parsed.
474
        * The purpose of having this list is to ensure that robots are properly identified for control purposes.
475
        * This keeps robot identification consistent between sessions and prevents arbitrary assignment.
476
        *
477
        * @param line the String containing XBee ID information.
478
        * @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots] IDs in the String
479
        * @see ColonetServerInterface#sendXBeeIDRequest()
480
        */
481
        public void parseXBeeIDs (String line) {
482
                String [] str = line.split(" ");
483
                int num = Integer.parseInt(str[2]);
484
                xbeeID = new int[num];
485
                for (int i = 0; i < num; i++) {
486
                        xbeeID[i] = Integer.parseInt(str[i+3]);
487
                }
488

    
489
                //update the list of robots to control
490
                //but save the old value first
491
                Object oldSelection = cmbRobotNum.getSelectedItem();
492
                cmbRobotNum.removeAllItems();
493
                cmbRobotNum.addItem(new String("         All         "));
494
                for (int i = 0; i < num; i++) {
495
                        cmbRobotNum.addItem(new String("" + xbeeID[i]));
496
                }
497
                cmbRobotNum.setSelectedItem(oldSelection);
498
                repaint();
499
        }
500

    
501
        /**
502
        * Parses a String containing battery information.
503
        * The ColonetServerInterface receives Strings of battery information.         (For encoding
504
        * information, see the ColonetServerInterface documentation.)         This method takes
505
        * a string of the form "[command code] [command code] [robot ID] [value]"
506
        * with tokens separated by spaces and containing no brackets.
507
        * The [command code]s are predefined values identifying this String as a battery
508
        * information String, [robot ID] is an integer, and [value] is a battery measurement.
509
        * This updates the batery information for a single robot.
510
        *
511
        *
512
        * @param line the String containing battery information.
513
        * @see ColonetServerInterface#sendBatteryRequest(int)
514
        */
515
        public void parseBattery (String line) {
516
            int botNum, level;
517
            System.out.println("Got battery update:" + line);
518
            try {
519
                    String [] str = line.split(" ");
520
                    botNum = Integer.parseInt(str[2]);
521
                    level = Integer.parseInt(str[3]);
522
                } catch (NumberFormatException e) {
523
                    System.out.println("Could not parse battery update");
524
                    return;
525
                }
526

    
527
                RobotIcon r = robotIcons.get(botNum);
528
                if (r != null) {
529
                    r.battery = batteryIcon.convert(level);  // set contextual battery meter
530
                    batteryIcon.setLevel(level); // set solo battery meter
531
                    System.out.println("Set " + r.id + " battery value to " + level);
532
                }
533
                repaint();
534
        }
535

    
536
        /**
537
        * Parses a String containing visual robot position information along with
538
        * canonical ID assignments.
539
        */
540
        public void parsePositions (String line) {
541
                String [] str = line.split(" ");
542
                java.util.Map <Integer, RobotIcon> newMap = new HashMap <Integer, RobotIcon> ();
543

    
544
                for (int i = 2; i < str.length; i+=3) {
545
                        int id = Integer.parseInt(str[i]);
546
                        int x = Integer.parseInt(str[i+1]);
547
                        int y = Integer.parseInt(str[i+2]);
548
                        RobotIcon newIcon = new RobotIcon(id, x, y);
549
                        // Save previous battery value
550
                        if (robotIcons.get(id) != null)
551
                            newIcon.battery = robotIcons.get(id).battery;
552
                        if (newIcon.id >= 0) {
553
                                newIcon.color = Color.GREEN;
554
                        }
555
                        newMap.put(id, newIcon);
556
                }
557
                robotIcons = newMap;
558
                repaint();
559
        }
560

    
561
        //
562
        // MouseListener methods
563
        //
564
        public void mousePressed(MouseEvent e) {
565
                //Start a new Thread to handle the MouseEvent
566
                (new MouseHandler(e)).start();
567
                repaint();
568
        }
569
        public void mouseExited(MouseEvent e) {
570
        }
571
        public void mouseEntered(MouseEvent e) {
572
        }
573
        public void mouseReleased(MouseEvent e) {
574
        }
575
        public void mouseClicked(MouseEvent e) {
576
        }
577
        public void mouseDragged(MouseEvent e) {
578
        }
579
        public void mouseMoved(MouseEvent e) {
580
        }
581

    
582
        //
583
        // KeyListener methods
584
        //
585
        public void keyPressed (KeyEvent e) {
586
                //Start a new Thread to handle the KeyEvent
587
                (new KeyHandler(e)).start();
588
                repaint();
589
        }
590
        public void keyReleased (KeyEvent e) {
591
        }
592
        public void keyTyped (KeyEvent e) {
593
        }
594

    
595
        //
596
        // ActionListener method
597
        //
598
        public void actionPerformed (ActionEvent e) {
599
                // Start a new Thread to handle the ActionEvent
600
                (new ActionHandler(e)).start();
601
                repaint();
602
        }
603

    
604
        class MouseHandler extends Thread {
605
                MouseEvent e;
606

    
607
                public MouseHandler (MouseEvent event) {
608
                        super("MouseHandler");
609
                        this.e = event;
610
                }
611

    
612
                public void run () {
613
                        Point pt = panelWebcam.convertClick(e);
614

    
615
                        // If we are selecting a waypoint (destination) for a specific bot
616
                        if (setWaypoint && setWaypointID >= 0) {
617
                                setWaypoint = false;
618
                                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
619
                                if (selectedBot < 0) {
620
                                        return;
621
                                }
622
                                
623
                                RobotIcon r = robotIcons.get(selectedBot);
624
                                if (r != null) {
625
                                        r.destx = pt.x;
626
                                        r.desty = pt.y;
627
                                        if (csi != null) {
628
                                                csi.sendAbsoluteMove(r.id, r.destx, r.desty);
629
                                        }
630
                                }
631

    
632
                                return;
633
                        }
634

    
635
                        // Right-click also means we are moving a robot
636
                        if (e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3) {
637
                                if (selectedBot < 0) {
638
                                        return;
639
                                }
640

    
641
                                RobotIcon r = robotIcons.get(selectedBot);
642
                                if (r != null) {
643
                                        r.destx = pt.x;
644
                                        r.desty = pt.y;
645
                                        if (csi != null) {
646
                                                csi.sendAbsoluteMove(r.id, r.destx, r.desty);
647
                                        }
648
                                }
649

    
650
                                return;
651
                        }
652

    
653
                        // If we are setting all waypoints
654
                        if (setWaypoint) {
655
                                setWaypoint = false;
656
                                panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
657
                                for (Map.Entry<Integer,RobotIcon> entry : robotIcons.entrySet()) {
658
                                        RobotIcon r = entry.getValue();
659
                                        r.destx = pt.x;
660
                                        r.desty = pt.y;
661
                                }
662
                                return;
663
                        }
664

    
665
                        // Otherwise, we are selecting a bot, or doing nothing
666
                        for (Map.Entry<Integer,RobotIcon> entry : robotIcons.entrySet()) {
667
                                RobotIcon r = entry.getValue();
668
                                if (r.contains(pt.x, pt.y)) {
669
                                        selectedBot = r.id;
670
                                        lblSelected.setText("" + r.id);
671
                                        // Try to select the clicked bot, if its XBee ID is detected.
672
                                        for (int j = 1; j < cmbRobotNum.getItemCount(); j++) {
673
                                                if (Integer.parseInt(cmbRobotNum.getItemAt(j).toString()) == selectedBot) {
674
                                                        cmbRobotNum.setSelectedIndex(j);
675
                                                }
676
                                        }
677
                                        return;
678
                                }
679
                        }
680

    
681
                }
682
        }
683

    
684
        class KeyHandler extends Thread {
685
                KeyEvent e;
686

    
687
                public KeyHandler (KeyEvent event) {
688
                        super("KeyHandler");
689
                        this.e = event;
690
                }
691

    
692
                public void run () {
693
                        int code = e.getKeyCode();
694
                        if (code == KeyEvent.VK_UP) {
695
                                vectorController.setMaxForward();
696
                                vectorController.sendToServer();
697
                        } else if (code == KeyEvent.VK_DOWN) {
698
                                vectorController.setMaxReverse();
699
                                vectorController.sendToServer();
700
                        } else if (code == KeyEvent.VK_LEFT) {
701
                                vectorController.setMaxLeft();
702
                                vectorController.sendToServer();
703
                        } else if (code == KeyEvent.VK_RIGHT) {
704
                                vectorController.setMaxRight();
705
                                vectorController.sendToServer();
706
                        } else if (code == KeyEvent.VK_S) {
707
                                vectorController.setZero();
708
                                vectorController.sendToServer();
709
                        }
710
                }
711
        }
712

    
713
        class ActionHandler extends Thread {
714
                ActionEvent e;
715

    
716
                public ActionHandler (ActionEvent event) {
717
                        super("ActionHandler");
718
                        this.e = event;
719
                }
720

    
721
                public void run () {
722
                        Object source = e.getSource();
723

    
724
                        // General Actions
725
                        if (source == btnConnect) {
726
                                if (csi == null) {
727
                                        connect();
728
                                } else {
729
                                        disconnect();
730
                                }
731
                        } else if (source == btnGetXBeeIDs) {
732
                                csi.sendXBeeIDRequest();
733
                        } else if (source == btnAssignID) {
734
                                String message;
735
                                if (selectedBot < 0) {
736
                                        message = "That robot is unidentified. Please specify its ID.";
737
                                } else {
738
                                        message = "That robot has ID " + selectedBot + ". You may reassign it now.";
739
                                }
740
                                String result = JOptionPane.showInputDialog(self, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
741
                                if (result == null) {
742
                                        return;
743
                                }
744
                                int newID = -1;
745
                                try {
746
                                        newID = Integer.parseInt(result);
747
                                } catch (Exception ex) {
748
                                        csi.warn("Invalid ID.");
749
                                        return;
750
                                }
751
                                // Assign new ID and update display
752
                                if (csi != null) {
753
                                        csi.sendIDAssignment(selectedBot, newID);
754
                                }
755
                                selectedBot = newID;
756
                                lblSelected.setText("" + newID);
757
                        } else if (source == btnF) { // Robot Movement Controls
758
                                vectorController.setMaxForward();
759
                                vectorController.sendToServer();
760
                        } else if (source == btnB) {
761
                                vectorController.setMaxReverse();
762
                                vectorController.sendToServer();
763
                        } else if (source == btnL) {
764
                                vectorController.setMaxLeft();
765
                                vectorController.sendToServer();
766
                        } else if (source == btnR) {
767
                                vectorController.setMaxRight();
768
                                vectorController.sendToServer();
769
                        } else if (source == btnActivate) {
770
                                vectorController.setZero();
771
                                vectorController.sendToServer();
772
                        } else if (source == btnCommand_MoveTo) { // Robot Commands (non-movement)
773
                                if (selectedBot < 0) {
774
                                        return;
775
                                }
776
                                panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
777
                                setWaypoint = true;
778
                                setWaypointID = selectedBot;
779
                        } else if (source == btnCommand_MoveAll) {
780
                                panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
781
                                setWaypoint = true;
782
                                setWaypointID = -1;
783
                        } else if (source == btnCommand_StopTask) {
784

    
785
                        } else if (source == btnCommand_ResumeTask) {
786

    
787
                        } else if (source == btnCommand_ChargeNow) {
788

    
789
                        } else if (source == btnCommand_StopCharging) {
790

    
791
                        } else if (source == btnAddTask) { // Queue Management
792
                                taskAddWindow.prompt();
793
                        } else if (source == btnRemoveTask) {
794
                                if (taskList.getSelectedIndex() >= 0) {
795
                                        csi.sendQueueRemove(taskList.getSelectedIndex());
796
                                }
797
                                csi.sendQueueUpdate();
798
                        } else if (source == btnMoveTaskUp) {
799
                                csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
800
                                csi.sendQueueUpdate();
801
                        } else if (source == btnMoveTaskDown) {
802
                                csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
803
                                csi.sendQueueUpdate();
804
                        } else if (source == btnUpdateTasks) {
805
                                csi.sendQueueUpdate();
806
                        }
807

    
808
                }
809
        }
810

    
811
        /*
812
        * DataUpdater thread.
813
        *                The purpose of this thread is to request data from the server at regular intervals.
814
        *
815
        */
816
        class DataUpdater extends Thread {
817
                final int DATAUPDATER_DELAY = 200;
818
                public DataUpdater () {
819
                        super("Colonet DataUpdater");
820
                }
821

    
822
                public synchronized void run () {
823
                        while (true) {
824
                                try {
825
                                        //request more data
826
                                        if (csi != null && csi.isReady()) {
827
                                                csi.sendPositionRequest();
828
                                                csi.sendXBeeIDRequest();
829
                                                for (Map.Entry<Integer,RobotIcon> entry : robotIcons.entrySet()) {
830
                                                        RobotIcon r = entry.getValue();
831
                                                           int id = r.id;
832
                                                            if (id >= 0) {
833
                                                                csi.sendBatteryRequest(id);
834
                                                                System.out.println("Sent battery request (" + id + ")");
835
                                                        }
836
                                                }
837
                                        }
838
                                        Thread.sleep(DATAUPDATER_DELAY);
839
                                } catch (InterruptedException e) {
840
                                        return;
841
                                } catch (NullPointerException e) {
842
                                    // This probably indicates that an object was destroyed by shutdown.
843
                                    return;
844
                                }
845
                        }
846
                }
847
        }
848

    
849
        /*
850
        * GraphicsPanel class
851
        * An extension of JPanel, designed for holding an image that will be repainted regularly.
852
        */
853
        class GraphicsPanel extends JPanel {
854
                protected Image img;
855

    
856
                public GraphicsPanel (Image img) {
857
                        this(img, true);
858
                }
859

    
860
                public GraphicsPanel (Image img, boolean isDoubleBuffered) {
861
                        super(isDoubleBuffered);
862
                        this.img = img;
863
                }
864

    
865
                public void paint (Graphics g) {
866
                        // Place the buffered image on the screen, inside the panel
867
                        g.drawImage(img, 0, 0, Color.WHITE, this);
868
                }
869
        }
870

    
871
        /*
872
        * WebcamPanel class
873
        * Enables more efficient image handling in a component-controlled environment
874
        */
875
        class WebcamPanel extends JPanel {
876
                final int BORDER = 16;        // this is arbitrary. it makes the image look nice inside a border.
877
                final int BATTERY_WIDTH = 50;
878
                final int BATTERY_HEIGHT = 10;
879
                volatile BufferedImage img;
880
                BufferedImage buffer;
881

    
882
                public WebcamPanel () {
883
                        super(true);
884
                }
885

    
886
                public synchronized void setImage (BufferedImage newimg) {
887
                        if (img != null) {
888
                                img.flush();
889
                        }
890
                        System.gc();
891
                        img = newimg;
892
                        repaint();
893
                }
894

    
895
                public synchronized void paint (Graphics g) {
896
                        if (img == null) {
897
                                return;
898
                        }
899

    
900
                        // Calculate scaling
901
                        int maxWidth = getWidth() - 2*BORDER;
902
                        int maxHeight = getHeight() - 2*BORDER;
903
                        double widthRatio = 1.0 * maxWidth / img.getWidth();
904
                        double heightRatio = 1.0 * maxHeight / img.getHeight();
905
                        double scale = 0;
906
                        int newWidth = 0;
907
                        int newHeight = 0;
908
                        int x = 0;
909
                        int y = 0;
910

    
911
                        if (widthRatio > heightRatio) {         //height is the limiting factor
912
                                scale = heightRatio;
913
                                newHeight = maxHeight;
914
                                newWidth = (int) (img.getWidth() * scale);
915
                                y = BORDER;
916
                                x = (maxWidth - newWidth) / 2 + BORDER;
917
                        } else {        //width is the limiting factor
918
                                scale = widthRatio;
919
                                newWidth = maxWidth;
920
                                newHeight = (int) (img.getHeight() * scale);
921
                                x = BORDER;
922
                                y = (maxHeight - newHeight) / 2 + BORDER;
923
                        }
924

    
925
                        // Draw everything onto the buffer
926
                        buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
927
                        Graphics2D bufferedGraphics = (Graphics2D)buffer.getGraphics();
928
                        bufferedGraphics.setColor(Color.GRAY);
929
                        bufferedGraphics.fillRect(0, 0, this.getWidth(), this.getHeight());
930
                        Image imgScaled = img.getScaledInstance(newWidth, newHeight, Image.SCALE_FAST);
931
                        bufferedGraphics.drawImage(imgScaled, x, y, this);
932

    
933

    
934
                        // Draw Identifiers and battery levels
935
                        if (robotIcons != null) {
936
                                bufferedGraphics.setStroke(new BasicStroke(2));
937
                                for (Map.Entry<Integer,RobotIcon> entry : robotIcons.entrySet()) {
938
                                        RobotIcon r = entry.getValue();
939
                                        bufferedGraphics.setColor(r.color);
940
                                        // Identifier circle
941
                                        int px = (int) (x + r.x * scale);
942
                                        int py = (int) (y + r.y * scale);
943
                                        bufferedGraphics.drawOval(px-RADIUS, py-RADIUS, 2*r.RADIUS, 2*r.RADIUS);
944
                                        // Battery
945
                                        if (r.battery >= 0) {
946
                                            int pixels = r.battery * BATTERY_WIDTH / 160;  // 160 should be fully charged
947
                                                if (r.battery > 50)
948
                                                    bufferedGraphics.setColor(Color.GREEN);
949
                                                else if (r.battery > 25)
950
                                                    bufferedGraphics.setColor(Color.YELLOW);
951
                                                else
952
                                                    bufferedGraphics.setColor(Color.RED);
953
                                                bufferedGraphics.fillRect(px+20, py+20, pixels, BATTERY_HEIGHT);
954
                                                bufferedGraphics.setColor(Color.BLACK);
955
                                                bufferedGraphics.drawRect(px+20, py+20, BATTERY_WIDTH, BATTERY_HEIGHT);
956
                                        }
957
                                        // If the robot has a destination, draw the vector
958
                                        if (r.destx >= 0) {
959
                                                bufferedGraphics.drawLine(px, py, (int)(x + r.destx * scale), (int)(y + r.desty * scale));
960
                                        }
961
                                }
962
                        }
963

    
964
                        // Identify currently-selected robot
965
                        RobotIcon r = robotIcons.get(selectedBot);
966
                        if (r != null) {
967
                                int px = (int) (x + r.x * scale);
968
                                int py = (int) (y + r.y * scale);
969
                                bufferedGraphics.setColor(Color.BLACK);
970
                                bufferedGraphics.drawOval(px-RADIUS-6, py-RADIUS-6, 2*r.RADIUS+12, 2*r.RADIUS+12);
971
                        }
972

    
973
                        //Display buffered content
974
                        g.drawImage(buffer, 0, 0, this);
975
                }
976

    
977
                /*
978
                * Convert a click on the webcam panel to a coordinate that is consistent with the
979
                * original size of the image that the panel contains.
980
                */
981
                public Point convertClick (MouseEvent e) {
982
                        if (img == null) {
983
                                return new Point(e.getX(), e.getY());
984
                        }
985

    
986
                        // Calculate scaling
987
                        int clickx = e.getX();
988
                        int clicky = e.getY();
989
                        int maxWidth = getWidth() - 2*BORDER;
990
                        int maxHeight = getHeight() - 2*BORDER;
991
                        double widthRatio = 1.0 * maxWidth / img.getWidth();
992
                        double heightRatio = 1.0 * maxHeight / img.getHeight();
993
                        double scale = 0;
994
                        int newWidth = 0;
995
                        int newHeight = 0;
996
                        int px = 0;
997
                        int py = 0;
998

    
999
                        if (widthRatio > heightRatio) {         //height is the limiting factor
1000
                                scale = heightRatio;
1001
                                newHeight = maxHeight;
1002
                                newWidth = (int) (img.getWidth() * scale);
1003
                                py = clicky - BORDER;
1004
                                px = clickx - BORDER - (maxWidth - newWidth) / 2;
1005
                        } else {        //width is the limiting factor
1006
                                scale = widthRatio;
1007
                                newWidth = maxWidth;
1008
                                newHeight = (int) (img.getHeight() * scale);
1009
                                px = clickx - BORDER;
1010
                                py = clicky - BORDER - (maxHeight - newHeight) / 2;
1011
                        }
1012
                        py = (int) (py / scale);
1013
                        px = (int) (px / scale);
1014
                        return new Point(px, py);
1015
                }
1016
        }
1017

    
1018
        /*
1019
        * WebcamLoader class
1020
        * Handles the loading of the webcam image.
1021
        */
1022
        class WebcamLoader extends Thread
1023
        {
1024
                final int WEBCAMLOADER_DELAY = 250;
1025
                final String IMAGE_PATH = "http://128.2.99.176/colonet.jpg";
1026

    
1027
                URL imagePath;
1028

    
1029
                MediaTracker mt;
1030
                BufferedImage image;
1031
                Random rand;
1032

    
1033
                public WebcamLoader (JApplet applet)
1034
                {
1035
                        super("ColonetWebcamLoader");
1036
                        mt = new MediaTracker(applet);
1037
                        ImageIO.setUseCache(false);
1038
                        rand = new Random();
1039
                }
1040

    
1041
                public void run ()
1042
                {
1043
                        while (true) {
1044
                                try {
1045
                                        Thread.sleep(WEBCAMLOADER_DELAY);
1046
                                        if (image != null)
1047
                                                image.flush();
1048
                                        System.gc();
1049
                                        try {
1050
                                                imagePath = new URL(IMAGE_PATH + "?rand=" + rand.nextInt(100000));
1051
                                        } catch (MalformedURLException e) {
1052
                                                System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n");
1053
                                        }
1054
                                        image = ImageIO.read(imagePath);
1055
                                        // The MediaTracker waitForID pauses the thread until the image is loaded.
1056
                                        // We don't want to display a half-downloaded image.
1057
                                        mt.addImage(image, 1);
1058
                                        mt.waitForID(1);
1059
                                        mt.removeImage(image);
1060
                                        // Save
1061
                                        panelWebcam.setImage(image);
1062
                                } catch (InterruptedException e) {
1063
                                        return;
1064
                                } catch (java.security.AccessControlException e) {
1065
                                        csi.warn("Could not load webcam.\n" + e);
1066
                                        return;
1067
                                } catch (IOException e) {
1068
                                        getInfoPanel().append("IOException while trying to load image.");
1069
                                }
1070
                        }
1071
                }
1072
        }
1073

    
1074
        /*
1075
        * VectorController class
1076
        * Manages robot motion control graphically
1077
        */
1078
        class VectorController extends GraphicsPanel implements MouseListener, MouseMotionListener {
1079
                int x, y, cx, cy;
1080
                final int WIDTH, HEIGHT;
1081
                final int SIDE;
1082
                final int BOT_SIZE = 70;
1083
        final int WHEEL_SIZE = 15;
1084

    
1085
                public VectorController (Image img) {
1086
                        super (img);
1087
                        WIDTH = img.getWidth(null);
1088
                        HEIGHT = img.getHeight(null);
1089
                        cx = WIDTH/2;
1090
                        cy = HEIGHT/2;
1091
                        x = cx;
1092
                        y = cy;
1093
                        if (WIDTH < HEIGHT) {
1094
                                SIDE = WIDTH;
1095
                        } else {
1096
                                SIDE = HEIGHT;
1097
                        }
1098
                        this.addMouseListener(this);
1099
                        this.addMouseMotionListener(this);
1100
                }
1101

    
1102
                public void setPoint (int x, int y) {
1103
                        if (isValidPoint(x, y)) {
1104
                                this.x = x;
1105
                                this.y = y;
1106
                        }
1107
                }
1108

    
1109
                public boolean isValidPoint (int x, int y) {
1110
                        double xsq = Math.pow(1.0*(x - cx)/(SIDE/2), 2);
1111
                        double ysq = Math.pow(1.0*(y - cy)/(SIDE/2), 2);
1112
                        return (xsq + ysq <= 1);
1113
                }
1114

    
1115
                public void notifyMouseEvent (MouseEvent e, boolean send) {
1116
                        if (!isValidPoint(e.getX(), e.getY())) {
1117
                                return;
1118
                        }
1119
                        setPoint(e.getX(), e.getY());
1120
                repaint();
1121
                        if (send) {
1122
                            Runnable r = new Runnable () {
1123
                                public void run () {
1124
                                    sendToServer();
1125
                                }
1126
                            };
1127
                                (new Thread(r)).start();
1128
                        }
1129
                }
1130

    
1131
                public void mouseExited(MouseEvent e) {
1132
                }
1133
                public void mouseEntered(MouseEvent e) {
1134
                }
1135
                public void mouseReleased(MouseEvent e) {
1136
                        this.notifyMouseEvent(e, true);
1137
                }
1138
                public void mouseClicked(MouseEvent e) {
1139
                        this.notifyMouseEvent(e, false);
1140
                }
1141
                public void mousePressed(MouseEvent e) {
1142
                }
1143
                public void mouseDragged(MouseEvent e) {
1144
                        vectorController.notifyMouseEvent(e, false);
1145
                }
1146
                public void mouseMoved(MouseEvent e) {
1147
                }
1148

    
1149
                public int getSpeed () {
1150
                        int dx = x - cx;
1151
                        int dy = y - cy;
1152
                        int s = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) );
1153
                        int maxspeed = SIDE/2;
1154
                        return s * 512 / SIDE;
1155
                }
1156

    
1157
                /**
1158
                * Returns the angle of the control vector in positive degrees west of north,
1159
                * or negative degrees east of north, whichever is less than or equal to
1160
                * 180 degrees total.
1161
                */
1162
                public int getAngle () {
1163
                        int dx = x - cx;
1164
                        int dy = cy - y;
1165
                        // find reference angle in radians
1166
                        double theta = Math.atan2(Math.abs(dx), Math.abs(dy));
1167
                        // transform to degrees
1168
                        theta = theta * 180 / Math.PI;
1169
                        // adjust for quadrant
1170
                        if (dx < 0 && dy < 0)
1171
                                theta = 90 + theta;
1172
                        else if (dx < 0 && dy >= 0)
1173
                                theta = 90 - theta;
1174
                        else if (dx >= 0 && dy < 0)
1175
                                theta = -90 - theta;
1176
                        else
1177
                                theta = -90 + theta;
1178
                        return (int) theta;
1179
                }
1180
                
1181
                public int getMotorL () {
1182
                    if (getSpeed() == 0)
1183
                        return 0;
1184
                    int dx = x - cx;
1185
                        int dy = (cy - y) * 255 / getSpeed();
1186
                        int val = 0;
1187
                    // Dependent on quadrant
1188
                        if (dx < 0 && dy < 0)
1189
                                val = -255;
1190
                        else if (dx < 0 && dy >= 0)
1191
                                val = dy * 1024 / SIDE - 255;
1192
                        else if (dx >= 0 && dy < 0)
1193
                                val = dy * 1024 / SIDE + 255;
1194
                        else
1195
                                val = 255;
1196
                    // Normalize to 0-255
1197
                        return val * getSpeed() / 255;
1198
                }
1199
                
1200
                public int getMotorR () {
1201
                    if (getSpeed() == 0)
1202
                        return 0;
1203
                    int dx = x - cx;
1204
                        int dy = (cy - y) * 255 / getSpeed();
1205
                        int val = 0;
1206
                    // Dependent on quadrant
1207
                        if (dx < 0 && dy < 0)
1208
                                val = dy * 1024 / SIDE + 255;
1209
                        else if (dx < 0 && dy >= 0)
1210
                                val = 255;
1211
                        else if (dx >= 0 && dy < 0)
1212
                                val = -255;
1213
                        else
1214
                                val = dy * 1024 / SIDE - 255;
1215
                        // Normalize to 0-255
1216
                        return val * getSpeed() / 255;
1217
                }
1218
                
1219
                private int eliminateDeadZone (int x) {
1220
                    final int START = 150;
1221
                    int val;
1222
                    if (x == 0)
1223
                        return 0;
1224
                    if (x > 0)
1225
                            val = (int) ((1 - 1.0 * START / 255) * x + START);
1226
                        else
1227
                            val = (int) ((1 - 1.0 * START / 255) * x - START);
1228
                        return val;
1229
                
1230
                }
1231

    
1232
                public void paint (Graphics g) {
1233
                        // Clear image
1234
                        g.setColor(Color.BLACK);
1235
                        g.fillRect(0, 0, WIDTH, HEIGHT);
1236
                        ((Graphics2D)g).setStroke(new BasicStroke(1));
1237
                        
1238
                        // Motor indicators
1239
                        int motor1 = getMotorL() * BOT_SIZE / 512;
1240
                        int motor2 = getMotorR() * BOT_SIZE / 512;
1241
                        g.setColor(Color.YELLOW);
1242
                        if (motor1 < 0)
1243
                            g.fillRect(cx-BOT_SIZE/2 - WHEEL_SIZE, cy, WHEEL_SIZE, -motor1);
1244
                        else
1245
                            g.fillRect(cx-BOT_SIZE/2 - WHEEL_SIZE, cy-motor1, WHEEL_SIZE, motor1);
1246
                        if (motor2 < 0)
1247
                            g.fillRect(cx+BOT_SIZE/2, cy, WHEEL_SIZE, -motor2);
1248
                        else
1249
                            g.fillRect(cx+BOT_SIZE/2, cy-motor2, WHEEL_SIZE, motor2);
1250
                        
1251
                        // Watermark
1252
                        g.setColor(Color.GRAY);
1253
                        g.drawOval(cx-BOT_SIZE/2, cy-BOT_SIZE/2, BOT_SIZE, BOT_SIZE);
1254
                        g.drawRect(cx-BOT_SIZE/2 - WHEEL_SIZE, cy-BOT_SIZE/2, WHEEL_SIZE, BOT_SIZE);
1255
                        g.drawRect(cx+BOT_SIZE/2, cy-BOT_SIZE/2, WHEEL_SIZE, BOT_SIZE);
1256
                        
1257
                        // Targeting circle
1258
                        g.setColor(Color.RED);
1259
                        g.drawOval(cx-SIDE/2, cy-SIDE/2, SIDE, SIDE);
1260
                        ((Graphics2D)g).setStroke(new BasicStroke(2));
1261
                        
1262
                        // Vector Line
1263
                        g.setColor(Color.GREEN);
1264
                        g.drawLine(cx, cy, x, y);
1265
                        g.fillOval(x-3, y-3, 6, 6);
1266
                }
1267

    
1268
                public void setMaxForward () {
1269
                        setPoint(cx, cy - (SIDE/2) + 1);
1270
                }
1271

    
1272
                public void setMaxReverse () {
1273
                        setPoint(cx, cy + (SIDE/2) - 1);
1274
                }
1275

    
1276
                public void setMaxLeft () {
1277
                        setPoint(cx - (SIDE/2) + 1, cy);
1278
                }
1279

    
1280
                public void setMaxRight () {
1281
                        setPoint(cx + (SIDE/2) - 1, cy);
1282
                }
1283

    
1284
                public void setZero () {
1285
                        setPoint(cx, cy);
1286
                }
1287

    
1288
                public void sendToServer () {
1289
                    // Determine destination ID
1290
                        String dest = ColonetServerInterface.GLOBAL_DEST;
1291
                        if (cmbRobotNum != null && cmbRobotNum.getSelectedIndex() > 0) {
1292
                                dest = (String)cmbRobotNum.getSelectedItem();
1293
                        }
1294

    
1295
                        if (csi == null)
1296
                            return;
1297

    
1298
                        String motor1_string;
1299
                        String motor2_string;
1300
                        int motor1 = eliminateDeadZone(getMotorL());
1301
                        int motor2 = eliminateDeadZone(getMotorR());
1302
                        
1303
                        if (motor1 > 0) {
1304
                            motor1_string = " 1 " + motor1;
1305
                        } else {
1306
                            motor1_string = " 0 " + (-motor1);
1307
                        }
1308
                        if (motor2 > 0) {
1309
                            motor2_string = " 1 " + motor2;
1310
                        } else {
1311
                            motor2_string = " 0 " + (-motor2);
1312
                        }
1313
                        
1314
                        csi.sendData(ColonetServerInterface.MOTOR1_SET + motor1_string, dest);
1315
                        csi.sendData(ColonetServerInterface.MOTOR2_SET + motor2_string, dest);
1316
                        
1317
                        /*
1318
                        // Directional commands
1319
                        if (x > cx && y == cy) {        //move right
1320
                                csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", dest);
1321
                                csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", dest);
1322
                        } else if (x < cx && y == cy) {         //move left
1323
                                csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", dest);
1324
                                csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", dest);
1325
                        } else if (x == cx && y > cy) {         //move forward
1326
                                csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 225", dest);
1327
                                csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 225", dest);
1328
                        } else if (x == cx && y < cy) {         //move backward
1329
                                csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 225", dest);
1330
                                csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 225", dest);
1331
                        } else if (x == cx && y == cy) {        //stop!
1332
                                csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 0", dest);
1333
                                csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 0", dest);
1334
                        }
1335
                        */
1336
                        
1337
                        /*
1338
                        // Atomic Directional commands
1339
                        if (x > cx && y == cy) {  //move right
1340
                                csi.sendData(ColonetServerInterface.MOVE_R, dest);
1341
                        } else if (x < cx && y == cy) {         //move left
1342
                                csi.sendData(ColonetServerInterface.MOVE_L, dest);
1343
                        } else if (x == cx && y > cy) {         //move forward
1344
                                csi.sendData(ColonetServerInterface.MOVE_F, dest);
1345
                        } else if (x == cx && y < cy) {         //move backward
1346
                                csi.sendData(ColonetServerInterface.MOVE_B, dest);
1347
                        } else if (x == cx && y == cy) { //stop
1348
                                csi.sendData(ColonetServerInterface.MOTORS_OFF, dest);
1349
                        }
1350
                        */
1351
                }
1352

    
1353
        }
1354

    
1355

    
1356

    
1357
}