Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (38.7 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