Project

General

Profile

Revision 528

Created a branch for wireless development.

View differences:

branches/wl_dev/code/projects/colonet/client/Colonet.java
1
//
2
//  Colonet.java
3
//
4

  
5
import javax.swing.*;
6
import javax.swing.event.*;
7
import javax.imageio.*;
8
import java.awt.*;
9
import java.awt.image.*;
10
import java.awt.event.*;
11
import java.net.*;
12
import java.io.*;
13
import java.util.*;
14

  
15

  
16
/**
17
*	The Colonet Graphical User Interface Applet for use locally and over an internet connection.
18
*	@author Gregory Tress
19
*
20
*	To generate javadoc on this file or other java files, use javadoc *.java -d doc, where doc
21
*	is the name of the folder into which the files should be written.
22
*/
23
public class Colonet extends JApplet implements ActionListener, MouseInputListener, KeyListener, Runnable {
24

  
25
	// Used for images
26
	final int CANVAS_SIZE = 500;  //the applet may be slow if the canvas gets too large
27
	final int BUFFER = 50;
28
	final int RADIUS = 30;
29

  
30
	//Used for the robot controller
31
	final int VECTOR_CONTROLLER_HEIGHT = 220;
32
	final int VECTOR_CONTROLLER_WIDTH = 350;
33

  
34

  
35
	// Connection
36
	JTextField txtHost;
37
	JTextField txtPort;
38
	JButton btnConnect;
39
	JButton btnGetXBeeIDs;
40
	JLabel lblConnectionStatus;
41
	JTextArea txtMatrix;
42
	JTextArea txtInfo;
43
	JPanel panelConnect;
44
	JPanel panelServerInterface;
45
	Socket socket;
46
	OutputStreamWriter out;
47
	DataUpdater dataUpdater;
48

  
49
	// South
50
	JPanel panelSouth;
51
	JTextArea log;
52
	JScrollPane spLog;
53

  
54
	// Control
55
	JPanel panelControl;
56
	JTabbedPane tabPaneControl;
57
	JPanel panelRobotControl;
58
	JPanel panelRobotDirection;
59
	JPanel panelRobotDirectionButtons;
60
	JPanel panelRobotCommands;
61
	JButton btnF, btnB, btnL, btnR, btnActivate;
62
	JComboBox cmbRobotNum;
63
	JLabel lblBattery;
64
	JLabel lblSelected;
65
	BatteryIcon batteryIcon;
66
	JPanel panelBattery;
67
	VectorController vectorController;
68
	BufferedImage imageVectorControl;
69
	JButton btnAssignID;
70
	boolean setWaypoint;
71
	int setWaypointID;
72
	JButton btnCommand_MoveTo;
73
	JButton btnCommand_MoveAll;
74
	JButton btnCommand_StopTask;
75
	JButton btnCommand_ResumeTask;
76
	JButton btnCommand_ChargeNow;
77
	JButton btnCommand_StopCharging;
78

  
79
	// Task Manager
80
	JPanel panelTaskManager;
81
	JScrollPane spTaskManager;
82
	JPanel panelTaskManagerControls;
83
	JPanel panelTaskManagerControlsPriority;
84
	DefaultListModel taskListModel;
85
	JList taskList;
86
	JButton btnAddTask;
87
	JButton btnRemoveTask;
88
	JButton btnMoveTaskUp;
89
	JButton btnMoveTaskDown;
90
	JButton btnUpdateTasks;
91
	TaskAddWindow taskAddWindow;
92

  
93
	//Webcam
94
	WebcamPanel panelWebcam;
95
	GraphicsPanel panelGraph;
96
	GraphicsConfiguration gc;
97
	volatile BufferedImage image;
98
	volatile Graphics2D canvas;
99
	int cx, cy;
100
	JTabbedPane tabPaneMain;
101

  
102
	Font botFont;
103
	volatile int numBots;
104
	volatile int selectedBot;  //the user has selected this bot graphically
105
	volatile java.util.List <RobotIcon> robotIcons;  //contains boundary shapes around bots for click detection
106
	volatile int[] xbeeID;
107

  
108
	Colonet self = this;
109
	Thread paintThread;
110
	WebcamLoader webcamLoader;
111
	ColonetServerInterface csi;
112

  
113

  
114
	public void init () {
115
		// Set the default look and feel - choose one
116
        //String laf = UIManager.getSystemLookAndFeelClassName();
117
		String laf = UIManager.getCrossPlatformLookAndFeelClassName();
118
		//String laf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
119
        try {
120
            UIManager.setLookAndFeel(laf);
121
        } catch (UnsupportedLookAndFeelException exc) {
122
            System.err.println ("Warning: UnsupportedLookAndFeel: " + laf);
123
        } catch (Exception exc) {
124
            System.err.println ("Error loading " + laf + ": " + exc);
125
        }
126
		// We should invoke and wait to avoid browser display difficulties
127
		Runnable r = new Runnable() {
128
			public void run() {
129
				createAndShowGUI();
130
			}
131
		};
132
		try {
133
			SwingUtilities.invokeAndWait(r);
134
		} catch (InterruptedException e) {
135
			//Not really sure why we would be in this situation
136
			System.out.println("InterruptedException in init: " + e);
137
		} catch (java.lang.reflect.InvocationTargetException e) {
138
			//This could happen for various reasons if there is a problem in createAndShowGUI
139
			e.printStackTrace();
140
		}
141
	}
142

  
143
	public void destroy () {
144
		try { paintThread.interrupt(); } catch (Exception e) { }
145
	}
146

  
147
	private synchronized void createAndShowGUI () {
148
		// init graphical elements
149
		// Get the graphics configuration of the screen to create a buffer
150
		gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
151
		image = gc.createCompatibleImage(CANVAS_SIZE,CANVAS_SIZE);
152
		canvas = image.createGraphics();
153
		canvas.setStroke(new BasicStroke(2));  //set pen width
154
		panelGraph = new GraphicsPanel(image, false);  //set automatic double-buffering to false. we are doing it manually.
155
		panelWebcam = new WebcamPanel();
156
		tabPaneMain = new JTabbedPane();
157
		tabPaneMain.add(panelWebcam, "Webcam");
158
		//tabPaneMain.add(panelGraph, "Graph");
159

  
160
		// Calculate center of canvas
161
		cx = image.getWidth() / 2;
162
		cy = image.getHeight() / 2;
163

  
164
		// Set up robots
165
		botFont = new Font("Arial", Font.PLAIN, 14);
166
		numBots = 0;
167
		selectedBot = -1;
168
		robotIcons = new ArrayList <RobotIcon> ();
169

  
170
		// Connection area
171
		txtMatrix = new JTextArea();
172
		txtMatrix.setBorder(BorderFactory.createTitledBorder("Info"));
173
		txtInfo = new JTextArea();
174
		txtInfo.setBorder(BorderFactory.createTitledBorder("Info"));
175
		txtInfo.setEditable(false);
176
		txtHost = new JTextField(this.getDocumentBase().getHost());
177
		txtHost.setBorder(BorderFactory.createTitledBorder("Host"));
178
		txtPort = new JTextField("10123");
179
		txtPort.setBorder(BorderFactory.createTitledBorder("Port"));
180
		btnConnect = new JButton("Connect");
181
		btnGetXBeeIDs = new JButton("Get XBee IDs");
182
		getRootPane().setDefaultButton(btnConnect);
183
		lblConnectionStatus = new JLabel("Status: Offline");
184
		panelConnect = new JPanel();
185
		panelConnect.setLayout(new GridLayout(6,1));
186
		panelConnect.add(lblConnectionStatus);
187
		panelConnect.add(txtHost);
188
		panelConnect.add(txtPort);
189
		panelConnect.add(btnConnect);
190
		//panelConnect.add(btnGetXBeeIDs);
191
		panelServerInterface = new JPanel();
192
		panelServerInterface.setLayout(new GridLayout(2,1));
193
		panelServerInterface.add(panelConnect);
194
		panelServerInterface.add(txtMatrix);
195

  
196
		// Robot direction panel
197
		panelRobotDirection = new JPanel();
198
		panelRobotDirectionButtons = new JPanel();
199
		btnF = new JButton("^");
200
		btnB = new JButton("v");
201
		btnL = new JButton("<");
202
		btnR = new JButton(">");
203
		btnActivate = new JButton("o");
204
		panelRobotDirectionButtons.setLayout(new GridLayout(1,5));
205
		panelRobotDirectionButtons.add(btnActivate);
206
		panelRobotDirectionButtons.add(btnF);
207
		panelRobotDirectionButtons.add(btnB);
208
		panelRobotDirectionButtons.add(btnL);
209
		panelRobotDirectionButtons.add(btnR);
210

  
211
		imageVectorControl = gc.createCompatibleImage(VECTOR_CONTROLLER_WIDTH, VECTOR_CONTROLLER_HEIGHT);
212
		vectorController = new VectorController(imageVectorControl);
213
		panelRobotDirection.setLayout(new BorderLayout());
214
		panelRobotDirection.add(vectorController, BorderLayout.CENTER);
215
		panelRobotDirection.add(panelRobotDirectionButtons, BorderLayout.SOUTH);
216

  
217
		// Robot Control and Commands
218
		panelRobotCommands = new JPanel();
219
		panelRobotCommands.setLayout(new GridLayout(5,2));
220
		cmbRobotNum = new JComboBox();
221
		// Battery subset
222
		batteryIcon = new BatteryIcon(0);
223
		lblBattery = new JLabel(batteryIcon);
224
		lblSelected = new JLabel("None");
225
		// Command subset
226
		setWaypoint = false;
227
		setWaypointID = -1;
228
		btnAssignID = new JButton("Assign ID");
229
		btnCommand_MoveTo = new JButton("Move to ...");
230
		btnCommand_MoveAll = new JButton("Move all ...");
231
		btnCommand_StopTask = new JButton("Stop Current Task");
232
		btnCommand_ResumeTask = new JButton("Resume Current Task");
233
		btnCommand_ChargeNow = new JButton("Recharge Now");
234
		btnCommand_StopCharging = new JButton("Stop Recharging");
235
		panelRobotCommands.add(new JLabel("Select Robot to Control: "));
236
		panelRobotCommands.add(cmbRobotNum);
237
		panelRobotCommands.add(new JLabel("Battery Level: "));
238
		panelRobotCommands.add(lblBattery);
239
		panelRobotCommands.add(new JLabel("Selected Icon: "));
240
		panelRobotCommands.add(lblSelected);
241
		panelRobotCommands.add(btnAssignID);
242
		panelRobotCommands.add(new JLabel(""));
243
		panelRobotCommands.add(btnCommand_MoveTo);
244
		panelRobotCommands.add(btnCommand_MoveAll);
245
		//panelRobotCommands.add(btnCommand_StopTask);
246
		//panelRobotCommands.add(btnCommand_ResumeTask);
247
		//panelRobotCommands.add(btnCommand_ChargeNow);
248
		//panelRobotCommands.add(btnCommand_StopCharging);
249
		panelRobotControl = new JPanel();
250
		panelRobotControl.setLayout(new GridLayout(2,1));
251
		panelRobotControl.add(panelRobotDirection);
252
		panelRobotControl.add(panelRobotCommands);
253

  
254

  
255
		// Task Manager
256
		panelTaskManager = new JPanel();
257
		panelTaskManager.setLayout(new BorderLayout());
258
		taskListModel = new DefaultListModel();
259
		taskList = new JList(taskListModel);
260
		taskList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
261
		taskList.setSelectedIndex(0);
262
		spTaskManager = new JScrollPane(taskList);
263
		panelTaskManagerControls = new JPanel();
264
		panelTaskManagerControls.setLayout(new GridLayout(1,4));
265
		panelTaskManagerControlsPriority = new JPanel();
266
		panelTaskManagerControlsPriority.setLayout(new GridLayout(1,2));
267
		btnAddTask = new JButton("Add...");
268
		btnRemoveTask = new JButton("Remove");
269
		btnMoveTaskUp = new JButton("^");
270
		btnMoveTaskDown = new JButton("v");
271
		btnUpdateTasks = new JButton("Update");
272
		panelTaskManagerControlsPriority.add(btnMoveTaskUp);
273
		panelTaskManagerControlsPriority.add(btnMoveTaskDown);
274
		panelTaskManagerControls.add(btnAddTask);
275
		panelTaskManagerControls.add(btnRemoveTask);
276
		panelTaskManagerControls.add(btnUpdateTasks);
277
		panelTaskManagerControls.add(panelTaskManagerControlsPriority);
278
		panelTaskManager.add(spTaskManager, BorderLayout.CENTER);
279
		panelTaskManager.add(panelTaskManagerControls, BorderLayout.SOUTH);
280
		panelTaskManager.add(new JLabel("Current Task Queue"), BorderLayout.NORTH);
281
		taskAddWindow = new TaskAddWindow();
282

  
283
		// Message log
284
		log = new JTextArea();
285
		spLog = new JScrollPane(log,
286
			ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
287
			ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
288
		spLog.setBorder(BorderFactory.createTitledBorder("Log"));
289
		spLog.setPreferredSize(new Dimension(0, 120));
290
		log.setEditable(false);
291

  
292
		// Main control mechanism
293
		panelControl = new JPanel();
294
		panelControl.setLayout(new GridLayout(1,1));
295
		tabPaneControl = new JTabbedPane(JTabbedPane.TOP);
296
		tabPaneControl.setPreferredSize(new Dimension(VECTOR_CONTROLLER_WIDTH, 0));
297
		tabPaneControl.addTab("Connection", panelServerInterface);
298
		tabPaneControl.addTab("Robots", panelRobotControl);
299
		//tabPaneControl.addTab("Tasks", panelTaskManager);
300
		panelControl.add(tabPaneControl);
301

  
302
		// Set up elements in the south
303
		panelSouth = new JPanel();
304
		panelSouth.setLayout(new GridLayout(1,2));
305
		//panelSouth.add(spLog);
306

  
307
		// Put all elements in the ContentPane
308
		this.getContentPane().setLayout(new BorderLayout());
309
		this.getContentPane().add(tabPaneMain, BorderLayout.CENTER);
310
		this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
311
		this.getContentPane().add(panelControl, BorderLayout.EAST);
312
		this.setVisible(true);
313

  
314
		/* Add all listeners here */
315
		// Task Management
316
		btnAddTask.addActionListener(this);
317
		btnRemoveTask.addActionListener(this);
318
		btnMoveTaskUp.addActionListener(this);
319
		btnMoveTaskDown.addActionListener(this);
320
		btnUpdateTasks.addActionListener(this);
321
		// Robot Control
322
		btnF.addActionListener(this);
323
		btnB.addActionListener(this);
324
		btnL.addActionListener(this);
325
		btnR.addActionListener(this);
326
		btnF.addKeyListener(this);
327
		btnB.addKeyListener(this);
328
		btnL.addKeyListener(this);
329
		btnR.addKeyListener(this);
330
		btnActivate.addActionListener(this);
331
		btnActivate.addKeyListener(this);
332
		cmbRobotNum.addKeyListener(this);
333
		btnCommand_MoveTo.addActionListener(this);
334
		btnCommand_MoveAll.addActionListener(this);
335
		btnCommand_StopTask.addActionListener(this);
336
		btnCommand_ResumeTask.addActionListener(this);
337
		btnCommand_ChargeNow.addActionListener(this);
338
		btnCommand_StopCharging.addActionListener(this);
339
		// Other
340
		btnConnect.addActionListener(this);
341
		btnGetXBeeIDs.addActionListener(this);
342
		btnAssignID.addActionListener(this);
343
		panelWebcam.addMouseListener(this);
344

  
345
	}
346

  
347
	public void run () {
348
		while (true) {
349
			repaint();
350
			try {
351
				Thread.sleep(90);
352
			} catch (InterruptedException e) {
353
				return;
354
			}
355
		}
356
	}
357

  
358
	public void paint (Graphics g) {
359
	    super.paint(g);
360
	}
361

  
362
	public void update (Graphics g) {
363
	    paint(g);
364
	}
365

  
366
	/**
367
	* Gets the JTextArea used for storing the activity log. This method returns a reference to the
368
	* JTextArea that stores the log. The log can contain any activity that is revelant to the use
369
	* of the applet, and may optionally display debugging information.
370
	*
371
	* @return the JTextArea where BOM matrix information is stored.
372
	*/
373
	public JTextArea getLog () {
374
		return log;
375
	}
376

  
377
	/**
378
	* Gets the JTextArea used for storing the BOM matrix data. This method returns a reference to the
379
	* JTextArea that stores the BOM matrix. The values in the matrix are stored as integers separated
380
	* by spaces, and the lines should be separated by a newline.
381
	*
382
	* @return the JTextArea where BOM matrix information is stored.
383
	*/
384
	public JTextArea getMatrixInput () {
385
		return txtMatrix;
386
	}
387

  
388
	/**
389
	* Parses a String containing BOM matrix information.
390
	* The ColonetServerInterface receives lines of the BOM matrix.  (For encoding
391
	* information, see the ColonetServerInterface documentation.)  The entire matrix is passed
392
	* to the client when requested. This method takes a string of the form
393
	* "[command code] [command code] [number of robots] [data0] [data1] ..."
394
	* with tokens separated by spaces and containing no brackets.
395
	* The [command code]s are predefined values identifying this String as a BOM data
396
	* String, [number of robots] is an integer, and the values that follow are
397
	* the sensor readings of the robots in order, starting with robot 0.  Only [number of robots]^2
398
	* data entries will be read.  The matrix values are saved locally until the next String is parsed.
399
	*
400
	*
401
	* @param line the String containing BOM matrix information.
402
	* @throws ArrayIndexOutOfBoundsException if there are fewer than [number of robots]^2 data entries in the String
403
	*/
404
	public void parseMatrix (String line) {
405
		txtMatrix.setText("");
406
		String [] str = line.split(" ");
407
		int num = Integer.parseInt(str[2]);
408
		for (int i = 0; i < num; i++) {
409
			for (int j = 0; j < num; j++) {
410
				String next = str[3 + i*num + j];
411
				if (next.equals("-1"))
412
					txtMatrix.append("-");
413
				else
414
					txtMatrix.append(next);
415
				if (j < num - 1)
416
					txtMatrix.append(" ");
417
			}
418
			if (i < num - 1)
419
				txtMatrix.append("\n");
420
		}
421
		repaint();
422
	}
423

  
424
	public void connect () {
425
	    lblConnectionStatus.setText("Status: Connecting...");
426
	    webcamLoader = new WebcamLoader(this);
427
		dataUpdater = new DataUpdater();
428
		csi = new ColonetServerInterface(this);
429
		csi.connect(txtHost.getText(), txtPort.getText());
430
		if (!csi.isReady()) {
431
		    lblConnectionStatus.setText("Status: Offline");
432
			return;
433
	    }
434
		btnConnect.setText("Disconnect");
435
		lblConnectionStatus.setText("Status: Connected");
436
		dataUpdater.start();
437
		webcamLoader.start();
438
	}
439

  
440
	public void disconnect () {
441
	    lblConnectionStatus.setText("Status: Disconnecting...");
442
	    dataUpdater.interrupt();
443
	    csi.disconnect();
444
	    csi = null;
445
	    btnConnect.setText("Connect");
446
	    lblConnectionStatus.setText("Status: Disconnected");
447
	}
448

  
449
	/**
450
	* Parses a String containing a task queue update.
451
	* Format is currently not specified.
452
	* This method currently does nothing.
453
	*
454
	* @param line the String containing task queue update information.
455
	*/
456
	public void parseQueue (String line) {
457
		log.append("Got queue update\n");
458
		//TODO: display new queue data in tasks tab
459
	}
460

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

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

  
496
	/**
497
	* Parses a String containing battery information.
498
	* The ColonetServerInterface receives Strings of battery information.  (For encoding
499
	* information, see the ColonetServerInterface documentation.)  This method takes
500
	* a string of the form "[command code] [command code] [robot ID] [value]"
501
	* with tokens separated by spaces and containing no brackets.
502
	* The [command code]s are predefined values identifying this String as a battery
503
	* information String, [robot ID] is an integer, and [value] is a battery measurement.
504
	* This updates the batery information for a single robot.
505
	*
506
	*
507
	* @param line the String containing battery information.
508
	* @see ColonetServerInterface#sendBatteryRequest(int)
509
	*/
510
	public void parseBattery (String line) {
511
		String [] str = line.split(" ");
512
		int botNum = Integer.parseInt(str[2]);
513
		int level = Integer.parseInt(str[3]);
514
		int selected = -1;
515
		try {
516
		    selected = Integer.parseInt((String)cmbRobotNum.getSelectedItem());
517
		} catch (Exception e) {
518
		}
519
		if (selected == botNum) {
520
			batteryIcon.setLevel(level);
521
		}
522
		repaint();
523
	}
524

  
525
	/**
526
	* Parses a String containing visual robot position information along with
527
	* canonical ID assignments.
528
	*/
529
	public void parsePositions (String line) {
530
		String [] str = line.split(" ");
531
		java.util.List <RobotIcon> newList = new ArrayList <RobotIcon> ();
532

  
533
		for (int i = 2; i < str.length; i+=3) {
534
			int id = Integer.parseInt(str[i]);
535
			int x = Integer.parseInt(str[i+1]);
536
			int y = Integer.parseInt(str[i+2]);
537
			RobotIcon newIcon = new RobotIcon(id, x, y);
538
            if (newIcon.id >= 0)
539
                newIcon.color = Color.GREEN;
540
			newList.add(newIcon);
541
		}
542
		robotIcons = newList;
543
		repaint();
544
	}
545

  
546

  
547
	//
548
	// MouseListener methods
549
	//
550
	public void mousePressed(MouseEvent e) {
551
	    //Start a new Thread to handle the MouseEvent
552
	    (new MouseHandler(e)).start();
553
	}
554
	public void mouseExited(MouseEvent e) {
555
	}
556
	public void mouseEntered(MouseEvent e) {
557
	}
558
	public void mouseReleased(MouseEvent e) {
559
	}
560
	public void mouseClicked(MouseEvent e) {
561
	}
562
	public void mouseDragged(MouseEvent e) {
563
	}
564
	public void mouseMoved(MouseEvent e) {
565
	}
566

  
567
	//
568
	// KeyListener methods
569
	//
570
	public void keyPressed (KeyEvent e) {
571
		//Start a new Thread to handle the KeyEvent
572
	    (new KeyHandler(e)).start();
573
	}
574
	public void keyReleased (KeyEvent e) {
575
	}
576
	public void keyTyped (KeyEvent e) {
577
	}
578

  
579
	//
580
	// ActionListener method
581
	//
582
	public void actionPerformed (ActionEvent e) {
583
		// Start a new Thread to handle the ActionEvent
584
		(new ActionHandler(e)).start();
585
	}
586

  
587
	class MouseHandler extends Thread {
588

  
589
	    MouseEvent e;
590

  
591
	    public MouseHandler (MouseEvent event) {
592
	        super("MouseHandler");
593
	        this.e = event;
594
	    }
595

  
596
	    public void run () {
597

  
598
	        Point pt = panelWebcam.convertClick(e);
599

  
600
	        // If we are selecting a waypoint (destination) for a specific bot
601
	        if (setWaypoint && setWaypointID  >= 0) {
602
	            setWaypoint = false;
603
	            panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
604
	            if (selectedBot < 0)
605
	                return;
606

  
607
	            RobotIcon r = robotIcons.get(selectedBot);
608
	            r.destx = pt.x;
609
	            r.desty = pt.y;
610

  
611
	            if (csi != null)
612
	                csi.sendAbsoluteMove(r.id, r.destx, r.desty);
613

  
614
	            return;
615
	        }
616

  
617
	        // Right-click also means we are moving a robot
618
	        if (e.getButton() == MouseEvent.BUTTON2 || e.getButton() == MouseEvent.BUTTON3) {
619
	            if (selectedBot < 0)
620
	                return;
621

  
622
	            RobotIcon r = robotIcons.get(selectedBot);
623
	            r.destx = pt.x;
624
	            r.desty = pt.y;
625

  
626
	            if (csi != null)
627
	                csi.sendAbsoluteMove(r.id, r.destx, r.desty);
628

  
629
	            return;
630
	        }
631

  
632
	        // If we are setting all waypoints
633
	        if (setWaypoint) {
634
	            setWaypoint = false;
635
	            panelWebcam.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
636
	            for (int i = 0; i < robotIcons.size(); i++) {
637
	                RobotIcon r = robotIcons.get(i);
638
	                r.destx = pt.x;
639
	                r.desty = pt.y;
640
	            }
641
	            return;
642
	        }
643

  
644
	        // Otherwise, we are selecting a bot, or doing nothing
645
		    for (int i = 0; i < robotIcons.size(); i++) {
646
		        RobotIcon r = robotIcons.get(i);
647
		        if (r.contains(pt.x, pt.y)) {
648
		            selectedBot = i;
649
		            lblSelected.setText(" " + r.id);
650
					// Try to select the clicked bot, if its XBee ID is detected.
651
					for (int j = 1; j < cmbRobotNum.getItemCount(); j++) {
652
						if (Integer.parseInt(cmbRobotNum.getItemAt(j).toString()) == robotIcons.get(selectedBot).id)
653
							cmbRobotNum.setSelectedIndex(j);
654
					}
655
					return;
656
		        }
657
		    }
658

  
659
		    repaint();
660
	    }
661
	}
662

  
663
	class KeyHandler extends Thread {
664

  
665
		KeyEvent e;
666

  
667
		public KeyHandler (KeyEvent event) {
668
			super("KeyHandler");
669
			this.e = event;
670
		}
671

  
672
		public void run () {
673
			int code = e.getKeyCode();
674
			if (code == KeyEvent.VK_UP) {
675
				vectorController.setMaxForward();
676
				vectorController.sendToServer();
677
			} else if (code == KeyEvent.VK_DOWN) {
678
				vectorController.setMaxReverse();
679
				vectorController.sendToServer();
680
			} else if (code == KeyEvent.VK_LEFT) {
681
				vectorController.setMaxLeft();
682
				vectorController.sendToServer();
683
			} else if (code == KeyEvent.VK_RIGHT) {
684
				vectorController.setMaxRight();
685
				vectorController.sendToServer();
686
			} else if (code == KeyEvent.VK_S) {
687
				vectorController.setZero();
688
				vectorController.sendToServer();
689
			}
690
			repaint();
691
		}
692
	}
693

  
694
	class ActionHandler extends Thread {
695

  
696
	    ActionEvent e;
697

  
698
	    public ActionHandler (ActionEvent event) {
699
	        super("ActionHandler");
700
	        this.e = event;
701
	    }
702

  
703
	    public void run () {
704
	        Object source = e.getSource();
705

  
706
		    // General Actions
707
		    if (source == btnConnect) {
708
		        if (csi == null)
709
			        connect();
710
			    else
711
			        disconnect();
712
		    } else if (source == btnGetXBeeIDs) {
713
			    csi.sendXBeeIDRequest();
714
		    } else if (source == btnAssignID) {
715
		        String message;
716
		        if (selectedBot < 0)
717
		            return;
718
		        int curID = robotIcons.get(selectedBot).id;
719
		        if (curID < 0)
720
		            message = "That robot is unidentified. Please specify its ID.";
721
		        else
722
		            message = "That robot has ID " + curID + ". You may reassign it now.";
723
		        String result = JOptionPane.showInputDialog(self, message, "Robot Identification", JOptionPane.QUESTION_MESSAGE);
724
		        if (result == null)
725
		            return;
726
	            int newID = -1;
727
		        try {
728
		            newID = Integer.parseInt(result);
729
		        } catch (Exception ex) {
730
		            csi.warn("Invalid ID.");
731
		            return;
732
		        }
733
		        // Assign new ID and update display
734
			    csi.sendIDAssignment(curID, newID);
735
		        robotIcons.get(selectedBot).id = newID;
736
		        robotIcons.get(selectedBot).color = Color.GREEN;
737
		        lblSelected.setText(" " + newID);
738

  
739

  
740
		    }
741

  
742
		    // Robot Movement Controls
743
		    else if (source == btnF) {
744
			    vectorController.setMaxForward();
745
			    vectorController.sendToServer();
746
		    } else if (source == btnB) {
747
			    vectorController.setMaxReverse();
748
			    vectorController.sendToServer();
749
		    } else if (source == btnL) {
750
			    vectorController.setMaxLeft();
751
			    vectorController.sendToServer();
752
		    } else if (source == btnR) {
753
			    vectorController.setMaxRight();
754
			    vectorController.sendToServer();
755
		    } else if (source == btnActivate) {
756
			    vectorController.setZero();
757
			    vectorController.sendToServer();
758
		    }
759
		    // Robot Commands (non-movement)
760
		    else if (source == btnCommand_MoveTo) {
761
		        if (selectedBot < 0)
762
		            return;
763
		        panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
764
		        setWaypoint = true;
765
		        setWaypointID = selectedBot;
766

  
767
		    } else if (source == btnCommand_MoveAll) {
768
		        panelWebcam.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
769
		        setWaypoint = true;
770
		        setWaypointID = -1;
771

  
772
		    } else if (source == btnCommand_StopTask) {
773

  
774
		    } else if (source == btnCommand_ResumeTask) {
775

  
776
		    } else if (source == btnCommand_ChargeNow) {
777

  
778
		    } else if (source == btnCommand_StopCharging) {
779

  
780
		    }
781

  
782
		    // Queue Management
783
		    else if (source == btnAddTask) {
784
			    taskAddWindow.prompt();
785
		    } else if (source == btnRemoveTask) {
786
			    if (taskList.getSelectedIndex() >= 0);
787
				    csi.sendQueueRemove(taskList.getSelectedIndex());
788
			    csi.sendQueueUpdate();
789
		    } else if (source == btnMoveTaskUp) {
790
			    csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() - 1);
791
			    csi.sendQueueUpdate();
792
		    } else if (source == btnMoveTaskDown) {
793
			    csi.sendQueueReorder(taskList.getSelectedIndex(), taskList.getSelectedIndex() + 1);
794
			    csi.sendQueueUpdate();
795
		    } else if (source == btnUpdateTasks) {
796
			    csi.sendQueueUpdate();
797
		    }
798

  
799
	        repaint();
800
	    }
801

  
802
	}
803

  
804
	/*
805
	*	DataUpdater thread.
806
	*   The purpose of this thread is to request data from the server at regular intervals.
807
	*
808
	*/
809
	class DataUpdater extends Thread {
810
		final int DATAUPDATER_DELAY = 400;
811

  
812
		public DataUpdater () {
813
			super("Colonet DataUpdater");
814
		}
815

  
816
		public void run () {
817
			String line;
818
			while (true) {
819
				try {
820
					//request more data
821
					if (csi != null && csi.isReady()) {
822
                        csi.sendPositionRequest();
823
						csi.sendXBeeIDRequest();
824
						if (cmbRobotNum.getSelectedIndex() > 0) {
825
						    String sel = (String) cmbRobotNum.getSelectedItem();
826
						    int num = Integer.parseInt(sel);
827
							csi.sendBatteryRequest(num);
828
						}
829
					}
830
					Thread.sleep(DATAUPDATER_DELAY);
831
				} catch (InterruptedException e) {
832
					return;
833
				}
834
			}
835
		}
836

  
837
	}
838

  
839
	/*
840
	*	GraphicsPanel class
841
	*	An extension of JPanel, designed for holding an image that will be repainted regularly.
842
	*/
843
	class GraphicsPanel extends JPanel {
844
		protected Image img;
845

  
846
		public GraphicsPanel (Image img) {
847
			this(img, true);
848
		}
849

  
850
		public GraphicsPanel (Image img, boolean isDoubleBuffered) {
851
			super(isDoubleBuffered);
852
			this.img = img;
853
		}
854

  
855
		public void paint (Graphics g) {
856
			// Place the buffered image on the screen, inside the panel
857
			g.drawImage(img, 0, 0, Color.WHITE, this);
858
		}
859

  
860
	}
861

  
862
	/*
863
	*	WebcamPanel class
864
	*	Enables more efficient image handling in a component-controlled environment
865
	*/
866
	class WebcamPanel extends JPanel {
867
		int BORDER = 16;  // this is arbitrary. it makes the image look nice inside a border.
868
		int BOT_RADIUS = 40;
869
		volatile BufferedImage img;
870
		BufferedImage buffer;
871

  
872
		public WebcamPanel () {
873
			super(true);
874
		}
875

  
876
		public synchronized void setImage (BufferedImage newimg) {
877
			if (img != null) {
878
				img.flush();
879
			}
880
			System.gc();
881
			img = newimg;
882
			repaint();
883
		}
884

  
885
		public synchronized void paint (Graphics g) {
886
			if (img == null)
887
				return;
888

  
889
			// Calculate scaling
890
			int maxWidth = getWidth() - 2*BORDER;
891
			int maxHeight = getHeight() - 2*BORDER;
892
			double widthRatio = 1.0 * maxWidth / img.getWidth();
893
			double heightRatio = 1.0 * maxHeight / img.getHeight();
894
			double scale = 0;
895
			int newWidth = 0;
896
			int newHeight = 0;
897
			int x = 0;
898
			int y = 0;
899

  
900
			if (widthRatio > heightRatio) {  //height is the limiting factor
901
			    scale = heightRatio;
902
			    newHeight = maxHeight;
903
			    newWidth = (int) (img.getWidth() * scale);
904
			    y = BORDER;
905
			    x = (maxWidth - newWidth) / 2 + BORDER;
906
			} else {  //width is the limiting factor
907
			    scale = widthRatio;
908
			    newWidth = maxWidth;
909
			    newHeight = (int) (img.getHeight() * scale);
910
			    x = BORDER;
911
			    y = (maxHeight - newHeight) / 2 + BORDER;
912
			}
913

  
914
			// Draw everything onto the buffer
915
			buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
916
			Graphics2D bufferedGraphics = (Graphics2D)buffer.getGraphics();
917
			bufferedGraphics.setColor(Color.GRAY);
918
			bufferedGraphics.fillRect(0, 0, this.getWidth(), this.getHeight());
919
			Image imgScaled = img.getScaledInstance(newWidth, newHeight, Image.SCALE_FAST);
920
			bufferedGraphics.drawImage(imgScaled, x, y, this);
921

  
922

  
923
			// Draw Identifiers and battery levels
924
			if (robotIcons != null) {
925
			    bufferedGraphics.setStroke(new BasicStroke(2));
926
			    for (int i = 0; i < robotIcons.size(); i++) {
927
				    RobotIcon r = robotIcons.get(i);
928
				    bufferedGraphics.setColor(r.color);
929
				    // Identifier circle
930
				    int px = (int) (x + r.x * scale);
931
				    int py = (int) (y + r.y * scale);
932
				    bufferedGraphics.drawOval(px-RADIUS, py-RADIUS, 2*r.RADIUS, 2*r.RADIUS);
933
				    // Battery
934
				    //if (r.battery >= 0) {
935
				        bufferedGraphics.setColor(Color.GREEN);
936
				        bufferedGraphics.fillRect(px+20, py+20, 30, 10);
937
				        bufferedGraphics.setColor(Color.BLACK);
938
				        bufferedGraphics.drawRect(px+20, py+20, 50, 10);
939
				    //}
940
				    // If the robot has a destination, draw the vector
941
				    if (r.destx >= 0) {
942
				        bufferedGraphics.drawLine(px, py, (int)(x + r.destx * scale), (int)(y + r.desty * scale));
943
				    }
944
			    }
945
			}
946

  
947
			// Identify currently-selected robot
948
			if (selectedBot >= 0) {
949
			    bufferedGraphics.setColor(Color.BLACK);
950
			    RobotIcon r = robotIcons.get(selectedBot);
951
			    int px = (int) (x + r.x * scale);
952
			    int py = (int) (y + r.y * scale);
953
			    bufferedGraphics.drawOval(px-RADIUS-6, py-RADIUS-6, 2*r.RADIUS+12, 2*r.RADIUS+12);
954
			}
955

  
956
			//Display buffered content
957
			g.drawImage(buffer, 0, 0, this);
958
		}
959

  
960
		/*
961
		* Convert a click on the webcam panel to a coordinate that is consistent with the
962
		* original size of the image that the panel contains.
963
		*/
964
		public Point convertClick (MouseEvent e) {
965
			// Calculate scaling
966
			int clickx = e.getX();
967
		    int clicky = e.getY();
968
			int maxWidth = getWidth() - 2*BORDER;
969
			int maxHeight = getHeight() - 2*BORDER;
970
			double widthRatio = 1.0 * maxWidth / img.getWidth();
971
			double heightRatio = 1.0 * maxHeight / img.getHeight();
972
			double scale = 0;
973
			int newWidth = 0;
974
			int newHeight = 0;
975
			int px = 0;
976
			int py = 0;
977

  
978
			if (widthRatio > heightRatio) {  //height is the limiting factor
979
			    scale = heightRatio;
980
			    newHeight = maxHeight;
981
			    newWidth = (int) (img.getWidth() * scale);
982
			    py = clicky - BORDER;
983
			    px = clickx - BORDER - (maxWidth - newWidth) / 2;
984
			    py *= scale;
985
			    px *= scale;
986
			} else {  //width is the limiting factor
987
			    scale = widthRatio;
988
			    newWidth = maxWidth;
989
			    newHeight = (int) (img.getHeight() * scale);
990
			    px = clickx - BORDER;
991
			    py = clicky - BORDER - (maxHeight - newHeight) / 2;
992
			    px *= scale;
993
			    py *= scale;
994
			}
995

  
996
		    return new Point(px, py);
997

  
998
		}
999

  
1000
	}
1001

  
1002
	/*
1003
	*	WebcamLoader class
1004
	*	Handles the loading of the webcam image.
1005
	*/
1006
	class WebcamLoader extends Thread
1007
	{
1008
		final int WEBCAMLOADER_DELAY = 400;
1009
		final String IMAGE_PATH = "http://roboclub9.frc.ri.cmu.edu/colonet.jpg";
1010

  
1011
		URL imagePath;
1012

  
1013
		MediaTracker mt;
1014
		BufferedImage image;
1015
		Random rand;
1016

  
1017
		public WebcamLoader (JApplet applet)
1018
		{
1019
			super("ColonetWebcamLoader");
1020
			mt = new MediaTracker(applet);
1021
			ImageIO.setUseCache(false);
1022
			rand = new Random();
1023
		}
1024

  
1025
		public void run ()
1026
		{
1027
			while (true) {
1028
				try {
1029
					Thread.sleep(WEBCAMLOADER_DELAY);
1030
					if (image != null)
1031
						image.flush();
1032
					System.gc();
1033
					try {
1034
				        imagePath = new URL(IMAGE_PATH + "?rand=" + rand.nextInt(50000));
1035
			        } catch (MalformedURLException e) {
1036
				        System.out.println("Malformed URL: could not form URL from: [" + IMAGE_PATH + "]\n");
1037
			        }
1038
					image = ImageIO.read(imagePath);
1039
					// The MediaTracker waitForID pauses the thread until the image is loaded.
1040
					// We don't want to display a half-downloaded image.
1041
					mt.addImage(image, 1);
1042
					mt.waitForID(1);
1043
					mt.removeImage(image);
1044
					// Save
1045
					panelWebcam.setImage(image);
1046
				} catch (InterruptedException e) {
1047
					return;
1048
				} catch (java.security.AccessControlException e) {
1049
					csi.warn("Could not load webcam.\n" + e);
1050
					return;
1051
				} catch (IOException e) {
1052
					log.append("IOException while trying to load image.");
1053
				}
1054
			}
1055
		}
1056

  
1057
	}
1058

  
1059
	/*
1060
	*  RobotIcon class
1061
	*  Provides a means for graphically representing and keeping track of webcam bots.
1062
	*/
1063
	class RobotIcon {
1064
		public final int RADIUS = 30;
1065
		public final int CLOSE = 80;
1066

  
1067
		public int x, y;
1068
		public int destx, desty;
1069
		public int id;
1070
		public Color color;
1071
        public int battery;
1072

  
1073
		public RobotIcon (int id, int x, int y) {
1074
			this.color = Color.RED;
1075
			this.x = x;
1076
			this.y = y;
1077
			this.id = id;
1078
			this.destx = -1;
1079
			this.desty = -1;
1080
			this.battery = -1;
1081
		}
1082

  
1083
		/**
1084
		*  Relocates this RobotIcon to a new coordinate point.
1085
		*
1086
		*/
1087
		public void move (int newX, int newY) {
1088
			this.x = newX;
1089
			this.y = newY;
1090
		}
1091

  
1092
		/**
1093
		*  Determines if a given point is within a reasonable range of the current location
1094
		*  to be considered the same robot when moving. The threshold is determined by the
1095
		*  CLOSE value.
1096
		*
1097
		*  @returns Whether or not the given point is reasonably close to the current location.
1098
		*
1099
		*/
1100
		public boolean isClose (int nx, int ny) {
1101
			int dist = (int) Point.distance(this.x, this.y, nx, ny);
1102
			return (dist < CLOSE);
1103
		}
1104

  
1105
		/**
1106
		*  Determines whether a given point is within the rectangle that circumscribes the
1107
		*  robot's circlular icon. Used for clicking on robots in webcam view.
1108
        *
1109
        */
1110
		public boolean contains (int px, int py) {
1111
		    Rectangle rect = new Rectangle(x-RADIUS, y-RADIUS, 2*RADIUS, 2*RADIUS);
1112
		    return rect.contains(px, py);
1113
		}
1114

  
1115
		public String toString () {
1116
			String s = "RobotIcon at (" + x + "," + y + "), id " + id;
1117
			return s;
1118
		}
1119

  
1120
	}
1121

  
1122

  
1123
	/*
1124
	*	VectorController class
1125
	*	Manages robot motion control graphically
1126
	*/
1127
	class VectorController extends GraphicsPanel implements MouseListener, MouseMotionListener {
1128
		int x, y, cx, cy;
1129
		int width, height;
1130
		int side;
1131

  
1132
		public VectorController (Image img) {
1133
			super (img);
1134
			width = img.getWidth(null);
1135
			height = img.getHeight(null);
1136
			cx = img.getWidth(null)/2;
1137
			cy = img.getHeight(null)/2;
1138
			x = cx;
1139
			y = cy;
1140
			if (width < height)
1141
				side = width;
1142
			else
1143
				side = height;
1144
			this.addMouseListener(this);
1145
			this.addMouseMotionListener(this);
1146
		}
1147

  
1148
		public void setPoint (int x, int y) {
1149
			if (!isValidPoint(x, y))
1150
				return;
1151
			this.x = x;
1152
			this.y = y;
1153
			repaint();
1154
		}
1155

  
1156
		public boolean isValidPoint (int x, int y) {
1157
			double xterm = Math.pow(1.0*(x - cx)/(side/2), 2);
1158
			double yterm = Math.pow(1.0*(y - cy)/(side/2), 2);
1159
			return (xterm + yterm <= 1);
1160
		}
1161

  
1162
		public void notifyMouseEvent (MouseEvent e, boolean send) {
1163
			if (!isValidPoint(e.getX(), e.getY()))
1164
				return;
1165
			vectorController.setPoint(e.getX(), e.getY());
1166
			vectorController.repaint();
1167
			if (send)
1168
				vectorController.sendToServer();
1169
		}
1170

  
1171
		public void mouseExited(MouseEvent e) {
1172
		}
1173
		public void mouseEntered(MouseEvent e) {
1174
		}
1175
		public void mouseReleased(MouseEvent e) {
1176
			this.notifyMouseEvent(e, true);
1177
		}
1178
		public void mouseClicked(MouseEvent e) {
1179
			this.notifyMouseEvent(e, false);
1180
		}
1181
		public void mousePressed(MouseEvent e) {
1182
		}
1183
		public void mouseDragged(MouseEvent e) {
1184
			vectorController.notifyMouseEvent(e, false);
1185
		}
1186
		public void mouseMoved(MouseEvent e) {
1187
		}
1188

  
1189
		public int getSpeed () {
1190
			int dx = x - cx;
1191
			int dy = y - cy;
1192
			int v = (int) Math.sqrt( Math.pow(dx, 2) + Math.pow(dy, 2) );
1193
			return v;
1194
		}
1195

  
1196
		/**
1197
		* Returns the angle of the control vector in positive degrees west of north,
1198
		* or negative degrees east of north, whichever is less than or equal to
1199
		* 180 degrees total.
1200
		*/
1201
		public int getAngle () {
1202
			int dx = x - cx;
1203
			int dy = cy - y;
1204
			// find reference angle in radians
1205
			double theta = Math.atan2(Math.abs(dx), Math.abs(dy));
1206
			// transform to degrees
1207
			theta = theta * 180 / Math.PI;
1208
			// adjust for quadrant
1209
			if (dx < 0 && dy < 0)
1210
				theta = 90 + theta;
1211
			else if (dx < 0 && dy >= 0)
1212
				theta = 90 - theta;
1213
			else if (dx >= 0 && dy < 0)
1214
				theta = -90 - theta;
1215
			else
1216
				theta = -90 + theta;
1217
			return (int) theta;
1218
		}
1219

  
1220
		public void paint (Graphics g) {
1221
			g.setColor(Color.BLACK);
1222
			g.fillRect(0, 0, width, height);
1223
			((Graphics2D)g).setStroke(new BasicStroke(1));
1224
			g.setColor(Color.RED);
1225
			g.drawOval(cx-side/2, cy-side/2, side, side);
1226
			((Graphics2D)g).setStroke(new BasicStroke(2));
1227
			g.setColor(Color.GREEN);
1228
			g.drawLine(cx, cy, x, y);
1229
			g.fillOval(x-3, y-3, 6, 6);
1230
		}
1231

  
1232
		public void setMaxForward () {
1233
			setPoint(cx, cy - (side/2) + 1);
1234
		}
1235

  
1236
		public void setMaxReverse () {
1237
			setPoint(cx, cy + (side/2) - 1);
1238
		}
1239

  
1240
		public void setMaxLeft () {
1241
			setPoint(cx - (side/2) + 1, cy);
1242
		}
1243

  
1244
		public void setMaxRight () {
1245
			setPoint(cx + (side/2) - 1, cy);
1246
		}
1247

  
1248
		public void setZero () {
1249
			setPoint(cx, cy);
1250
		}
1251

  
1252
		public void sendToServer () {
1253
			System.out.println("Attempting to send angle = " + getAngle() + ", speed = " + getSpeed() + "");
1254
			String dest = ColonetServerInterface.GLOBAL_DEST;
1255
			if (cmbRobotNum != null && cmbRobotNum.getSelectedIndex() > 0) {
1256
				dest = (String)cmbRobotNum.getSelectedItem();
1257
			}
1258

  
1259
			if (csi != null) {
1260
				/*
1261
				csi.sendData(ColonetServerInterface.MOVE + " " + getSpeed() + " " + getAngle(), dest);
1262
				*/
1263

  
1264
				//Directional commands
1265
				if (x > cx && y == cy) {  //move right
1266
					csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 200", dest);
1267
					csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 200", dest);
1268
				} else if (x < cx && y == cy) {  //move left
1269
					csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 200", dest);
1270
					csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 200", dest);
1271
				} else if (x == cx && y > cy) {  //move forward
1272
					csi.sendData(ColonetServerInterface.MOTOR2_SET + " 0 225", dest);
1273
					csi.sendData(ColonetServerInterface.MOTOR1_SET + " 0 225", dest);
1274
				} else if (x == cx && y < cy) {  //move backward
1275
					csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 225", dest);
1276
					csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 225", dest);
1277
				} else if (x == cx && y == cy) {  //stop!
1278
					csi.sendData(ColonetServerInterface.MOTOR2_SET + " 1 0", dest);
1279
					csi.sendData(ColonetServerInterface.MOTOR1_SET + " 1 0", dest);
1280
				}
1281
			}
1282
		}
1283

  
1284
	}
1285

  
1286
	/*
1287
	*	TaskAddWindow class
1288
	*	A window that provides a simple way to add tasks to a task queue.
1289
	*/
1290
	class TaskAddWindow extends JFrame implements ActionListener, ListSelectionListener {
1291
		JPanel panelButtons;
1292
		JPanel panelParameters;
1293
		JPanel panelSouth;
1294
		JPanel panelSelection;
1295
		JButton btnSubmit;
1296
		JButton btnCancel;
1297
		DefaultListModel availableListModel;
1298
		JList availableList;
1299
		JScrollPane spAvailableTasks;
1300
		JTextArea txtDescription;
1301
		JTextField txtParameters;
1302

  
1303
		public TaskAddWindow () {
1304
			super("Add a Task");
1305
			super.setSize(500,500);
1306
			super.setLayout(new BorderLayout());
1307

  
1308
			// set up buttons
1309
			btnSubmit = new JButton("Submit");
1310
			btnCancel = new JButton("Cancel");
1311
			panelButtons = new JPanel();
1312
			panelButtons.setLayout(new FlowLayout());
1313
			panelButtons.add(btnSubmit);
1314
			panelButtons.add(btnCancel);
1315
			this.getRootPane().setDefaultButton(btnSubmit);
1316

  
1317
			// set up task list
1318
			availableListModel = new DefaultListModel();
1319
			availableListModel.addElement("Map the Environment");
1320
			availableListModel.addElement("Clean Up Chemical Spill");
1321
			availableListModel.addElement("Grow Plants");
1322
			availableListModel.addElement("Save the Cheerleader");
1323
			availableListModel.addElement("Save the World");
1324
			availableList = new JList(availableListModel);
1325
			availableList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1326
			availableList.setSelectedIndex(-1);
1327
			spAvailableTasks = new JScrollPane(availableList);
1328
			spAvailableTasks.setBorder(BorderFactory.createTitledBorder("Select A Task"));
1329
			txtDescription = new JTextArea();
1330
			txtDescription.setEditable(false);
1331
			txtDescription.setLineWrap(true);
1332
			txtDescription.setWrapStyleWord(true);
1333
			txtDescription.setBorder(BorderFactory.createTitledBorder("Description"));
1334

  
1335
			//set up parameter area
1336
			panelParameters = new JPanel();
1337
			panelParameters.setLayout(new BorderLayout());
1338
			txtParameters = new JTextField();
1339
			panelParameters.add(new JLabel("Optional parameters for this task: "), BorderLayout.WEST);
1340
			panelParameters.add(txtParameters);
1341

  
1342
			// assemble objects
1343
			panelSelection = new JPanel();
1344
			panelSelection.setLayout(new GridLayout(1,2));
1345
			panelSelection.add(spAvailableTasks);
1346
			panelSelection.add(txtDescription);
1347

  
1348
			panelSouth = new JPanel();
1349
			panelSouth.setLayout(new GridLayout(2,1));
1350
			panelSouth.add(panelParameters);
1351
			panelSouth.add(panelButtons);
1352

  
1353
			this.getContentPane().add(panelSouth, BorderLayout.SOUTH);
1354
			this.getContentPane().add(panelSelection, BorderLayout.CENTER);
1355
			this.setLocationRelativeTo(null);
1356

  
1357
			// add listeners here
1358
			availableList.addListSelectionListener(this);
1359
			btnSubmit.addActionListener(this);
1360
			btnCancel.addActionListener(this);
1361
		}
1362

  
1363
		public void prompt () {
1364
			this.setVisible(true);
1365
		}
1366

  
1367
		private String getDescription (int index) {
1368
			if (index < 0)
1369
				return "";
1370
			switch (index) {
1371
				case 0: return "SLAM and junk";
1372
				case 1: return "I'm not sure this works";
1373
				case 2: return "Push them into the light";
1374
				case 3: return "...";
1375
				case 4: return "...";
1376

  
1377
				default: return "Task not recognized";
1378
			}
1379
		}
1380

  
1381
		public void actionPerformed (ActionEvent e) {
1382
			Object source = e.getSource();
1383
			if (source == btnSubmit) {
1384
				txtParameters.setText(txtParameters.getText().trim());
1385

  
1386

  
1387
				this.setVisible(false);
1388
			} else if (source == btnCancel) {
1389
				this.setVisible(false);
1390
			}
1391
		}
1392

  
1393
		public void valueChanged (ListSelectionEvent e) {
1394
			int index = availableList.getSelectedIndex();
1395
			if (index >= 0)
1396
				txtDescription.setText(getDescription(index));
1397
		}
1398

  
1399
	}
1400

  
1401
	/*
1402
	*  BatteryIcon class
1403
	*  Graphical representation of battery level
1404
	*/
1405
	class BatteryIcon implements Icon {
1406
		private int width;
1407
	    private int height;
1408
	    private int level;
1409

  
1410
		/**
1411
		* Constructs a new BatteryIcon with all default parameters.
1412
		* Default width and height are 50.
1413
		* Default level is 100.
1414
		*/
1415
	    public BatteryIcon(){
1416
	    	this(100, 50, 50);
1417
	    }
1418

  
1419
		/**
1420
		* Constructs a new BatteryIcon with default width and height, and with the specified level.
1421
		* Default width and height are 50.
1422
		*/
1423
	    public BatteryIcon(int startLevel){
1424
	    	this(startLevel, 50, 50);
1425
	    }
1426

  
1427
		/**
1428
		* Constructs a new BatteryIcon with the specified level, width, and height.
1429
		*/
1430
	    public BatteryIcon(int startLevel, int w, int h){
1431
	    	level = startLevel;
1432
	    	width = w;
1433
	    	height = h;
1434
	    }
1435

  
1436
	    public void paintIcon(Component c, Graphics g, int x, int y) {
1437
	        Graphics2D g2d = (Graphics2D) g.create();
1438
	        //clear the background
1439
	        g2d.setColor(Color.WHITE);
1440
	        g2d.fillRect(x + 1, y + 1, width - 2, height - 2);
1441
	        //outline
1442
	        g2d.setColor(Color.BLACK);
1443
	        g2d.drawRect((int)(x + width*.3), y + 2, (int)(width*.4), height - 4);
1444
	        //battery life rectangle
1445
			if (level > 50)
1446
				g2d.setColor(Color.GREEN);
1447
			else if (level > 25)
1448
				g2d.setColor(Color.YELLOW);
1449
			else
1450
				g2d.setColor(Color.RED);
1451
	        int greenX = (int)(x + 1 + width*.3);
1452
	        int greenY = (int)((y+3) + Math.abs(level-100.0)*(height-6)/(100));
1453
	        int greenWidth = (int)(width*.4 - 2)+1;
1454
	        int greenHeight = 1+(int)(level-0.0)*(height-6)/(100);
1455
	        g2d.fillRect(greenX, greenY, greenWidth, greenHeight);
1456
	        //text
1457
	        g2d.setColor(Color.BLACK);
1458
	        g2d.drawString(level + "%", greenX + greenWidth/2 - 10, greenY + greenHeight/2 + 5);
1459

  
1460
	        g2d.dispose();
1461
	    }
1462

  
1463
		/**
1464
		* Sets the battery level for this BatteryIcon. The level should be given in raw form, i.e. 0-255 directly
1465
		* from the robot. The value will be converted to a representative percentage automatically.
1466
		*
1467
		* @param newLevel the new battery reading from the robot that this BatteryIcon will display.
1468
		*/
1469
	    public void setLevel(int newLevel) {
1470
	    	level = convert(newLevel);
1471
	    	repaint();
1472
	    	System.out.println("Updated level to " + level);
1473
	    }
1474

  
1475
	    public int getIconWidth() {
1476
	        return width;
1477
	    }
1478

  
1479
	    public int getIconHeight() {
1480
	        return height;
1481
	    }
1482

  
1483
		/**
1484
		* Converts a robot battery reading into representable form.
1485
		* Readings from the robot are returned as raw values, 0-255. This method converts the reading into a value
1486
		* from 0 to 100 so that the practical remaining charge is represented.
1487
		*
1488
		* @param level The battery level as returned by the robot.
1489
		* @returns The representable battery percentage.
1490
		*/
1491
		private int convert (int level) {
1492
			// TODO: make this a forreals conversion.
1493
			return (int) (100.0 * level / 170);
1494
		}
1495

  
1496
	}
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff